Como dividir um arquivo de texto em vários arquivos de texto

4

Eu tenho um arquivo de texto chamado entry.txt que contém o seguinte:

[ entry1 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3633 3634 3636 3690 3691 3693 3766
3767 3769 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5628 5629 5631
[ entry2 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4526
4527 4529 4583 4584 4586 4773 4774 4776 5153 5154
5156 5628 5629 5631
[ entry3 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4241
4242 4244 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5495 5496 5498 5628 5629 5631

Gostaria de dividi-lo em três arquivos de texto: entry1.txt , entry2.txt , entry3.txt . Seu conteúdo é o seguinte.

entry1.txt :

[ entry1 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3633 3634 3636 3690 3691 3693 3766
3767 3769 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5628 5629 5631

entry2.txt :

[ entry2 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4526
4527 4529 4583 4584 4586 4773 4774 4776 5153 5154
5156 5628 5629 5631

entry3.txt :

[ entry3 ]
1239 1240 1242 1391 1392 1394 1486 1487 1489 1600
1601 1603 1657 1658 1660 2075 2076 2078 2322 2323
2325 2740 2741 2743 3082 3083 3085 3291 3292 3294
3481 3482 3484 3690 3691 3693 3766 3767 3769 4241
4242 4244 4526 4527 4529 4583 4584 4586 4773 4774
4776 5153 5154 5156 5495 5496 5498 5628 5629 5631

Em outras palavras, o caractere [ indica que um novo arquivo deve começar.

Existe alguma maneira de realizar a divisão automática de arquivos de texto? Minha eventual entrada real entry.txt contém 200.001 entradas.

Fazer o texto dividido no Windows ou no Linux seria ótimo. Eu não tenho acesso a uma máquina Mac. Obrigado!

    
por Andrew 26.08.2012 / 00:00

5 respostas

3

E aqui está uma frase simples e simples:

$ gawk '{if(match($0, /^\[ (.+?) \]/, k)){name=k[1]}} {print >name".txt" }' entry.txt

Isso funcionará para qualquer tamanho de arquivo, independentemente do número de linhas em cada entrada, desde que cada cabeçalho de entrada se pareça com [ blahblah blah blah ] . Observe o espaço logo após a abertura [ e pouco antes do fechamento ] .

EXPLICAÇÃO:

awk e gawk leem um arquivo de entrada linha a linha. Conforme cada linha é lida, seu conteúdo é salvo na variável $0 . Aqui, estamos dizendo ao awk para combinar qualquer coisa entre colchetes e salvar sua correspondência na matriz k .

Assim, toda vez que a expressão regular for correspondida, ou seja, para cada cabeçalho no seu arquivo, k [1] terá a região correspondente da linha. Ou seja, "entry1", "entry2" ou "entry3" ou "entryN". name=k[1] apenas salva o valor de k [1] (a correspondência) em uma nova variável name .

Finalmente, imprimimos cada linha em um arquivo chamado <whatever value k currently has>.txt , ie entry1.txt, entry2.txt ... entryN.txt.

Este método será muito mais rápido que o perl para arquivos maiores.

Não posso garantir isso, pois nunca usei o shell do Windows, mas estou disposto a apostar que ele será muito mais rápido do que isso também. Gawk / awk são RÁPIDOS.

    
por 26.08.2012 / 03:23
4

Para uma solução do Windows, experimente este script do PowerShell:

$Path = "D:\Scripts\PS\test"
$InputFile = (Join-Path $Path "log.txt")
$Reader = New-Object System.IO.StreamReader($InputFile)

While (($Line = $Reader.ReadLine()) -ne $null) {
    If ($Line -match "\[ (.+?) \]") {
        $OutputFile = $matches[1] + ".txt"
    }

    Add-Content (Join-Path $Path $OutputFile) $Line
}

Edite as variáveis $Path e $InputFile de acordo. Com algumas pequenas modificações, também poderia aceitar essa informação como parâmetros de linha de comando, ou você poderia transformá-la em uma função.

    
por 26.08.2012 / 00:57
3

Ainda outra solução awk :

BEGIN { 
  RS="\[ entry[0-9]+ \]\n"  # Record separator
  ORS=""                      # Reduce whitespace on output
}
NR == 1 { f=RT }              # Entries are of-by-one relative to matched RS
NR  > 1 {
  split(f, a, " ")            # Assuming entries do not have spaces 
  print f  > a[2] ".txt"      # a[2] now holds the bare entry name
  print   >> a[2] ".txt"
  f = RT                      # Remember next entry name
}
    
por 26.08.2012 / 04:23
2

O seguinte script perl faz o trabalho:

#!/usr/bin/perl

while (<STDIN>) {
    if ($_ =~ m/^\[ (.+?) \]/) {
        $f = $1;
        close FH if tell(FH) != -1;
        open FH, ">", "$f.txt" or die "couldn't open file $f: $!\n";
    }
    print FH $_;
}
close FH;

Execute o script assim:

script.pl < entry.txt

O script funciona, não importa quantas seções de entrada estejam incluídas e por quanto tempo as seções estejam, contanto que apenas os cabeçalhos da seção de entrada sejam como [ some text ] .

Se você preferir um código ilegível ou simplesmente não deseja armazenar um script em algum lugar, você pode usar este único comando:
perl -e 'while(<STDIN>){if($_=~/^\[ (.+?) \]/){close FH if tell FH!=-1;open FH,">","$1.txt"or die"$1.txt: $!";}print FH $_;}close FH;' < entry.txt
    
por 26.08.2012 / 00:42
2

Não é mais simples usar comandos existentes? Nem tudo precisa de um novo programa.

csplit / \ [/ file

    
por 04.09.2012 / 14:07