dividir um arquivo simples de 60 GB com registros ocasionalmente abrangendo várias linhas

dividir um arquivo simples de 60 GB com registros ocasionalmente abrangendo várias linhas

A entrada manual de dados torna o banco de dados poluído com registros contendo vários caracteres de nova linha. Para bons registros delimitados por aspas duplas no início e no final em um enorme arquivo simples de 60 GB com apenas uma única coluna, eles devem sempre abranger apenas uma linha como esta:

"Sequências completas de numerosos genomas mitocondriais, muitos procarióticos e vários genomas nucleares estão agora disponíveis."

Para registros inválidos, eles abrangem um número indefinido de múltiplas linhas como esta:

“O tabagismo atual foi forte e inversamente associado a riscos de alto risco.

padrões, após ajuste para fatores de risco concomitantes. Em relação a nunca

fumantes, os fumantes atuais eram significativamente menos propensos a ter uma doença de alto risco

padrão. "

Esses registros multilinhas proíbem a divisão de arquivos downstream pelo comando UNIX split. splitnão consegue reconhecer de forma inteligente essas múltiplas linhas como um único registro e isso pode levar à divisão de um único registro em arquivos separados. O Perl abaixo é muito lento para mesclar essas linhas para os registros inválidos primeiro para este arquivo enorme antes de dividir, já que $count não pode ser impresso após esperar por mais 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);

Qualquer comando UNIX útil para resolver este problema para que o arquivo de saída fique "limpo" com apenas registros de linha única que podem ser processados ​​por split?

sedparece ser uma opção, mas nenhuma das postagens a seguir responde a esta pergunta:

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 os padrões dessas postagens são muito regulares e constantes.

Responder1

Usando sedpara unir apenas as linhas divididas

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

leva 6 segundos para um arquivo de 10 MB no meu sistema. Isso seria 10 horas para 60 GB.

bbeé um pouco mais rápido

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

mas ainda leva 4 segundos.

Receio que essas linguagens de script não sejam a ferramenta para um bom desempenho em arquivos extremamente grandes. Que tal escrever um pequeno programa em C?

Responder2

exemplo usando gawk:

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

Isso diz dividir o arquivo inputem qualquer sequência "seguida por uma nova linha ( \n). Isso ignorará as novas linhas que não seguem imediatamente as aspas, preservando os registros multilinhas. Neste exemplo, a saída é gravada em um arquivo de texto, mas se você remover a > n".txt"parte, poderá enviar registros para um pipeline.

Responder3

Você Perlestá lento por causa do forloop que está sendo usado para ler o arquivo. Você realmente deveria estar usando o whileloop, já que o forloop carrega o arquivo inteiro na memória de uma só vez. É por isso que está demorando uma eternidade para imprimir $count.

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

informação relacionada