¿Cómo elimino todas las líneas de un archivo de menos de 6 caracteres?

17

Tengo un archivo que contiene aproximadamente 10 millones de líneas.

Quiero eliminar todas las líneas del archivo que tengan menos de seis caracteres.

¿Cómo hago esto?

Dime por qué
fuente
¿No es esta pregunta más adecuada para Stackoverflow?
user1073075
2
@ user1073075 está perfectamente en el tema aquí.
Seth

Respuestas:

30

Hay muchas maneras de hacer esto.

Utilizando grep:

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

Ahora out.txtcontendrá líneas que tienen seis o más caracteres.

Manera inversa:

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

Usando sed, eliminando líneas de longitud 5 o menos:

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

Forma inversa, líneas de impresión de longitud seis o más:

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

Puede guardar la salida en un archivo diferente usando el >operador como grepo editar el archivo en el lugar usando la -iopción de sed:

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

Se realizará una copia de seguridad del archivo original file.txt.baky se modificará el archivo modificado file.txt.

Si no desea mantener una copia de seguridad:

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

Usando shell, más lento, no hagas esto , esto es solo para mostrar otro método:

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

Utilizando python, incluso más lento que 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')

Mejor usar la comprensión de la lista para ser más 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')
heemayl
fuente
¡Hurra! Esperaba una respuesta de python =)
TellMeWhy
@DevRobot Ya veo ... luego echa un vistazo a la lista de comprensión que agregué, sé más pitón ...
heemayl
1
Además, @DevRobot no está tan seguro de que Python sea más lento en archivos grandes, cuando se usa la primera opción. En realidad, estoy bastante seguro de que Python es más rápido en millones de líneas, ya que se lee por línea.
Jacob Vlijm
1
El segundo ejemplo de Python lee todo el archivo en la memoria antes de hacer la unión. Creo que el primer ejemplo de Python es mejor en este caso.
Holloway
La lectura por líneas es necesariamente más lenta porque los archivos no están estructurados de esa manera. De todos modos, debe leer un bloque adelante y buscar una nueva línea con posibilidades reducidas de paralelización, luego devolver solo la cadena parcial. Necesitas un búfer circular. Debe asignar memoria dinámicamente si no sabe cuánto pueden durar las líneas.
The Vee
19

Es muy simple:

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

Esto es extremadamente eficiente, ya grepque no intentará analizar más de lo que necesita, ni interpretará los caracteres de ninguna manera: simplemente envía una línea (completa) a stdout (que el shell luego redirige al archivo de resultados) tan pronto como vio 6 caracteres en esa línea ( .en un contexto regexp coincide con cualquier 1 carácter).

Por lo tanto, grep solo generará líneas que tengan 6 (o más) caracteres, y las otras no se generarán por grep, por lo que no se convertirán en archivos de resultados.

Olivier Dulac
fuente
14

Solución # 1: usando C

La forma más rápida: compila y ejecuta este programa en 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;
}

Compile con gcc program.c -o program, ejecute con ./program file line_length(where file= ruta al archivo y line_length= longitud mínima de línea, en su caso 6; la longitud máxima de línea está limitada a 1000000caracteres por línea; puede cambiar esto cambiando el valor de MAX_BUFFER_SIZE).

(Truco para sustituir \ncon \0encontrado aquí .)

Comparación con todas las otras soluciones propuestas para esta pregunta, excepto la solución de shell (prueba ejecutada en un archivo de ~ 91MB con 10M líneas con una longitud promedio de 8 caracteres):

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

Solución # 2: usando AWK:

awk 'length>=6' file
  • length>=6: si length>=6devuelve VERDADERO, imprime el registro actual.

Solución # 3: usando Perl:

perl -lne 'length>=6&&print' file
  • Si lenght>=6devuelve VERDADERO, imprime el registro actual.

% 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
kos
fuente
1
Créeme ... estaba esperando tu awk solución ...
heemayl
2
@heemayl Y no vi la pregunta de inmediato, así que sabía que si estuvieras en línea habrías sido más rápido. Tuve que eliminar mi sedsolución (sucede, lo sé). XD
kos
¿Cuál es el punto de la posvariable? Entiendo que devuelve un puntero al personaje linecon un carácter de nueva línea, pero parece que nunca lo usas. Y si no lo encuentra, simplemente configúrelo igual a \0.
user1717828
@ user1717828 Si lo encuentro , lo reemplazo con \0( strchr()devuelve un puntero NULL si no se encuentra el carácter). El punto es reemplazar cada nueva línea al final de cada línea \0para que la nueva línea nunca se cuente por strlen(): esto es para que la longitud siempre se pueda comparar con 6, independientemente de una posible nueva línea faltante en la última línea. Tratar de manera diferente solo la última línea sería mucho más eficiente, lo sé. Probablemente actualizaré esto más tarde.
kos
1
@tripleee La idea era agregar una solución útil para algo más que un trabajo de una sola vez, o para archivos aún más grandes, pero : probé la grepsolución en el mismo archivo y en realidad es más rápido (probablemente porque strlen()no es la mejor idea aquí) . Intentaré usar un getchar()bucle para verificar solo el primer carácter N, supongo que eso debería mejorarlo visiblemente. Y sí, cualquier línea sobre la longitud del búfer simplemente se corta a la longitud del búfer.
kos
2

Puede usar Vim en modo Ex:

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

  2. .{6} encontrar líneas con 6 o más caracteres

  3. v invertir selección

  4. d Eliminar

  5. x guardar y cerrar

Steven Penny
fuente
1

Solución de rubí:

$ cat input.txt                                                                                                          
abcdef
abc
abcdefghijk

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

Idea simple: redirija el archivo al stdin de ruby ​​e imprima la línea desde el stdin solo si su longitud es mayor o igual a 6

Sergiy Kolodyazhnyy
fuente