divide um arquivo simples de 60 GB com registros ocasionalmente abrangendo várias linhas [fechadas]

1

A entrada manual de dados torna um 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 arquivo simples de 60 GB com uma única coluna, eles devem sempre abranger apenas uma linha como esta:

"Complete sequences of numerous mitochondrial, many prokaryotic, and several nuclear genomes are now available. "

Para registros incorretos, eles abrangem um número indefinido de várias linhas como esta:

"Current smoking was strongly and inversely associated with high-risk

patterns, after adjustment for concomitant risk factors. Relative to never

smokers, current smokers were significantly less likely to have a high-risk

pattern. "

Esses registros de várias linhas proíbem a divisão de arquivos downstream pelo comando split do UNIX. split não pode reconhecer inteligentemente 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 ruins primeiro para este arquivo enorme antes de dividir, pois $ count não pode ser impresso depois de 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 acessível para resolver este problema, para que o arquivo de saída seja "limpo", com apenas registros de linha única que podem ser processados por split ?

sed parece ser uma opção, mas nenhuma das postagens a seguir responde a essa pergunta:

link

link

link

porque os padrões desses posts são muito regulares e constantes.

    
por hubx 31.05.2017 / 14:50

3 respostas

3

Usando sed para unir somente 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.

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

    
por 31.05.2017 / 15:51
0

exemplo usando gawk :

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

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

    
por 31.05.2017 / 15:58
0

Seu Perl está lento porque o loop for está sendo usado para ler o arquivo. Você deve estar realmente usando o loop while , pois o loop for carrega o arquivo inteiro na memória de uma só vez. É por isso que demora uma eternidade para imprimir $ count.

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

Tags