dividir un archivo plano de 60 GB con registros que ocasionalmente abarcan varias líneas

dividir un archivo plano de 60 GB con registros que ocasionalmente abarcan varias líneas

La entrada manual de datos contamina la base de datos con registros que contienen múltiples caracteres de nueva línea. Para buenos registros delimitados por comillas dobles al principio y al final en un enorme archivo plano de 60 GB con una sola columna, siempre deben abarcar una sola línea como esta:

"Ya están disponibles secuencias completas de numerosos genomas mitocondriales, muchos procarióticos y varios nucleares".

Para registros incorrectos, abarcan un número indefinido de líneas múltiples como esta:

"El tabaquismo actual se asoció fuerte e inversamente con un alto riesgo

patrones, después del ajuste por factores de riesgo concomitantes. Relativo a nunca

fumadores, los fumadores actuales tenían significativamente menos probabilidades de tener un riesgo alto

patrón. "

Estos registros de varias líneas prohíben la división de archivos posteriores mediante el comando UNIX split. splitno puede reconocer inteligentemente esas líneas múltiples como un solo registro y esto puede llevar a dividir un solo registro en archivos separados. El Perl siguiente es demasiado lento para fusionar primero esas líneas de los registros incorrectos de este archivo enorme antes de dividirlo, ya que $count no se puede imprimir después de esperar más de 2 horas.

$file=$ARGV[0];
open(INFO, $file) or die("Could not open $file.");
open(OUT, ">out") or die("Could not open $file.");

$mergedline = "";
$count=0;
foreach $line (<INFO>)  {
    print $count++;
    if ($line =~ /^".*"\n$/) {
                print OUT $line;
                $mergedline = "";
                next;
        } elsif ($line =~ /"\n$/) {
                print OUT $mergedline;
                $mergedline = "";
                next;
        } else {
                chomp $line;
                $mergedline .= $line;
        }
}
close(INFO);

¿Algún comando UNIX útil para resolver este problema de modo que el archivo de salida esté "limpio" con solo registros de una sola línea que puedan ser procesados ​​por split?

sedparece ser una opción, pero ninguna de las siguientes publicaciones responde a esta pregunta:

https://stackoverflow.com/questions/15758814/turning-multiple-lines-into-one-line-with-comma-separated-perl-sed-awk

https://stackoverflow.com/questions/11290616/sed-conditional-merge-of-multiple-lines

http://www.unix.com/shell-programming-and-scripting/80633-sed-combining-multiple-lines-into-one.html

porque sus patrones de estas publicaciones son demasiado regulares y constantes.

Respuesta1

Usar sedpara unir solo las líneas divididas

sed ':a
/".*"$/b
N;s/\n/ /;ba' input >> output

Toma 6 segundos para un archivo de 10 MB en mi sistema. Serían 10 horas para 60 GB.

bbees un poco mas rapido

bbe -b '/"/:/"/' -o output -e 'y/\n/ /' input

pero todavía toma 4 segundos.

Me temo que esos lenguajes de secuencias de comandos no son la herramienta adecuada para funcionar bien en archivos extremadamente grandes. ¿Qué tal escribir un pequeño programa en C?

Respuesta2

ejemplo usando gawk:

awk 'BEGIN {RS = "\"\n"} {++n; print $0"\""> n".txt"}' input

Esto dice dividir el archivo inputen cualquier secuencia "seguido de una nueva línea ( \n). Esto ignorará las nuevas líneas que no siguen inmediatamente a una comilla, preservando los registros de varias líneas. En este ejemplo, la salida se escribe en un archivo de texto, pero si eliminó la > n".txt"parte, podría enviar registros a una canalización.

Respuesta3

Es Perllento debido a que forse utiliza el bucle para leer el archivo. Realmente deberías usar el whilebucle, ya que el forbucle carga todo el archivo en la memoria de una sola vez. Es por eso que se tarda una eternidad en imprimir $count.

perl -ne '
   print,next if /^".*"$/m or /"$/m;
   chomp, $_ .= <>, redo unless eof;
' gene.data

información relacionada