Come rimuovo tutte le righe di un file con meno di 6 caratteri?


17

Ho un file contenente circa 10 milioni di righe.

Voglio rimuovere tutte le righe nel file che sono meno di sei caratteri.

Come faccio a fare questo?


Questa domanda non è più adatta a StackOverflow?
user1073075

2
@ user1073075 è perfettamente in argomento qui.
Seth,

Risposte:


30

Ci sono molti modi per farlo.

Utilizzando grep:

grep -E '^.{6,}$' file.txt >out.txt

Ora out.txtconterrà righe con sei o più caratteri.

Modo inverso:

grep -vE '^.{,5}$' file.txt >out.txt

Utilizzo sed, rimozione di linee di lunghezza 5 o inferiore:

sed -r '/^.{,5}$/d' file.txt

Modo inverso, linee di stampa di lunghezza sei o più:

sed -nr '/^.{6,}$/p' file.txt 

È possibile salvare l'output in un altro file utilizzando un >operatore simile grepo modificare il file sul posto utilizzando l' -iopzione di sed:

sed -ri.bak '/^.{6,}$/' file.txt 

Verrà eseguito il backup del file originale così come file.txt.baklo sarà il file modificato file.txt.

Se non si desidera conservare un backup:

sed -ri '/^.{6,}$/' file.txt

Utilizzando shell, Slower, Non farlo , questo è solo per il gusto di mostrare un altro metodo:

while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt

Utilizzando python, anche più lento di grep, sed:

#!/usr/bin/env python2
with open('file.txt') as f:
    for line in f:
        if len(line.rstrip('\n')) >= 6:
            print line.rstrip('\n')

Meglio usare la comprensione dell'elenco per essere più Pythonic:

#!/usr/bin/env python2
with open('file.txt') as f:
     strip = str.rstrip
     print '\n'.join([line for line in f if len(strip(line, '\n')) >= 6]).rstrip('\n')

Sìì! Speravo in una risposta python =)
TellMePerché il

@DevRobot Vedo ... quindi ho verificato la comprensione dell'elenco che ho aggiunto, sii più Pythonic ..
heemayl

1
Inoltre @DevRobot non è così sicuro che python sia più lento su file di grandi dimensioni, quando viene utilizzata la prima opzione. In realtà sono abbastanza sicuro che Python sia più veloce su milioni di righe, poiché legge per riga.
Jacob Vlijm,

1
Il secondo esempio di Python legge l'intero file in memoria prima di eseguire il join. Penso che il primo esempio di Python sia migliore in questo caso.
Holloway,

La lettura per riga è necessariamente più lenta perché i file non sono strutturati in questo modo. Devi comunque leggere un blocco in avanti e cercare una nuova riga con ridotte possibilità di parallelizzazione, quindi restituire solo la stringa parziale. Hai bisogno di un buffer circolare. È necessario allocare la memoria in modo dinamico se non si conosce la lunghezza delle linee.
The Vee

19

È molto semplice:

grep ...... inputfile > resultfile   #There are 6 dots

Questo è estremamente efficiente, in quanto grepnon tenterà di analizzare più del necessario, né di interpretare i caratteri in alcun modo: invia semplicemente una (intera) linea a stdout (che la shell reindirizza quindi al file dei risultati) non appena ha visto 6 caratteri su quella riga ( .in un contesto regexp corrisponde a 1 carattere qualsiasi).

Quindi grep produrrà solo linee con 6 (o più) caratteri, e gli altri non vengono emessi da grep in modo che non riescano a ottenere il risultato.


14

Soluzione n. 1: utilizzo di C

Il modo più veloce: compilare ed eseguire questo programma C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BUFFER_SIZE 1000000

int main(int argc, char *argv[]) {
    int length;

    if(argc == 3)
        length = atoi(argv[2]);
    else
        return 1;

    FILE *file = fopen(argv[1], "r");

    if(file != NULL) {
        char line[MAX_BUFFER_SIZE];

        while(fgets(line, sizeof line, file) != NULL) {
            char *pos;

            if((pos = strchr(line, '\n')) != NULL)
                *pos = '\0';
            if(strlen(line) >= length)
                printf("%s\n", line);
        }

        fclose(file);
    }
    else {
        perror(argv[1]);
        return 1;
    }

    return 0;
}

Compila gcc program.c -o program, esegui con ./program file line_length(dove file= percorso del file e line_length= lunghezza minima della riga, nel tuo caso6 ; la lunghezza massima della riga è limitata ai 1000000caratteri per riga; puoi cambiarla cambiando il valore di MAX_BUFFER_SIZE).

(Trucco per sostituire \ncon \0trovato qui .)

Confronto con tutte le altre soluzioni proposte a questa domanda tranne la soluzione shell (test eseguito su un file di ~ 91MB con linee da 10M con una lunghezza media di 8 caratteri):

time ./foo file 6

real    0m1.592s
user    0m0.712s
sys 0m0.160s

time grep ...... file

real    0m1.945s
user    0m0.912s
sys 0m0.176s

time grep -E '^.{6,}$'

real    0m2.178s
user    0m1.124s
sys 0m0.152s

time awk 'length>=6' file

real    0m2.261s
user    0m1.228s
sys 0m0.160s

time perl -lne 'length>=6&&print' file

real    0m4.252s
user    0m3.220s
sys 0m0.164s

sed -r '/^.{,5}$/d' file >out

real    0m7.947s
user    0m7.064s
sys 0m0.120s

./script.py >out
real    0m8.154s
user    0m7.184s
sys 0m0.164s

Soluzione n. 2: utilizzando AWK:

awk 'length>=6' file
  • length>=6: se length>=6restituisce VERO, stampa il record corrente.

Soluzione n. 3: utilizzando Perl:

perl -lne 'length>=6&&print' file
  • Se lenght>=6restituisce VERO, stampa il record corrente.

% cat file
a
bb
ccc
dddd
eeeee
ffffff
ggggggg
% ./foo file 6
ffffff
ggggggg
% awk 'length>=6' file   
ffffff
ggggggg
% perl -lne 'length>=6&&print' file
ffffff
ggggggg

1
Credetemi ... stavo aspettando la vostra awk soluzione ...
heemayl

2
@heemayl E non ho visto immediatamente la domanda, quindi sapevo che se ti fosse capitato di essere online saresti stato più veloce. Ho dovuto eliminare la mia sedsoluzione (succede, lo so). XD
kos

Qual è il punto della posvariabile? Capisco che restituisce un puntatore al personaggio linecon un carattere newline, ma sembra che non lo usi mai. E se non lo trovi, lo imposti a uguale \0.
user1717828,

@ user1717828 Se lo trovo lo sostituisco con \0( strchr()restituisce un puntatore NULL se il carattere non viene trovato). Il punto sta sostituendo ogni nuova riga alla fine di ogni riga in \0modo che la nuova riga non venga mai conteggiata da strlen(): questo è così che la lunghezza può sempre essere confrontata con 6 indipendentemente da una potenziale nuova riga mancante nell'ultima riga. Trattare diversamente solo l'ultima riga sarebbe molto più efficiente, lo so. Probabilmente lo aggiornerò più tardi.
kos

1
@tripleee L'idea era di aggiungere una soluzione utile per qualcosa di più di un lavoro una tantum, o per file ancora più grandi, ma : ho provato la grepsoluzione sullo stesso file ed è effettivamente più veloce (probabilmente perché strlen()non è la migliore idea qui) . Proverò ad usare un getchar()loop per controllare invece solo il primo carattere N, suppongo che dovrebbe migliorarlo visibilmente. E sì, qualsiasi linea sulla lunghezza del buffer viene semplicemente tagliata alla lunghezza del buffer.
kos

2

Puoi usare Vim in modalità Ex:

ex -sc 'v/\v.{6}/d' -cx file
  1. \v accendi la magia

  2. .{6} trova righe con 6 o più caratteri

  3. v inverti selezione

  4. d Elimina

  5. x salva e chiudi


1

Soluzione ruby:

$ cat input.txt                                                                                                          
abcdef
abc
abcdefghijk

$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt                                                              
abcdef
abcdefghijk

Idea semplice: reindirizzare il file nello stdin di Ruby e stampare la linea dallo stdin solo se è maggiore o uguale a 6

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.