4

У меня есть текстовый файл с именем entry.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 ]
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

Я хотел бы разделить его на три текстовых файла: entry1.txt , entry2.txt , entry3.txt . Их содержание заключается в следующем.

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

Другими словами, символ [ указывает, что должен начинаться новый файл.

Есть ли способ, которым я могу выполнить автоматическое разбиение текстового файла? Мой возможный, фактический вход entry.txt самом деле содержит 200 001 записей.

Было бы здорово разделить текст в Windows или Linux. У меня нет доступа к компьютеру Mac. Спасибо!

5 ответов5

4

Для решения Windows попробуйте этот сценарий 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
}

Отредактируйте переменные $Path и $InputFile соответственно. С некоторыми незначительными изменениями он может также принять эту информацию в качестве параметров командной строки или превратить ее в функцию.

3

И вот хороший, простой gawk с одним вкладышем:

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

Это будет работать для любого размера файла, независимо от количества строк в каждой записи, при условии, что каждый заголовок записи выглядит как [ blahblah blah blah ] . Обратите внимание на пространство сразу после открытия [ и перед закрытием ] .


ОБЪЯСНЕНИЕ:

awk и gawk читают входной файл построчно. Когда каждая строка читается, ее содержимое сохраняется в переменной $0 . Здесь мы сообщаем awk о соответствии чему-либо в квадратных скобках и сохраняем его соответствие в массиве k .

Таким образом, каждый раз, когда сопоставляется регулярное выражение, то есть для каждого заголовка в вашем файле, k [1] будет соответствовать области строки. А именно, "entry1", "entry2" или "entry3" или "entryN". name=k[1] просто сохраняет значение k [1] (совпадение) в новом name переменной.

Наконец, мы печатаем каждую строку в файл с именем <whatever value k currently has>.txt , то есть entry1.txt, entry2.txt ... entryN.txt.

Этот метод будет намного быстрее, чем Perl для больших файлов.

Я не могу ручаться за это, поскольку я никогда не использовал Windows Shell, но я готов поспорить, что это будет гораздо быстрее, чем это. Gawk/awk БЫСТРЫ.

3

Еще одно решение 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
}
2

Не проще ли использовать существующие команды? Не всем нужна новая программа.

csplit /\[/ file

2

Следующий скрипт на Perl делает эту работу:

#!/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;

Запустите скрипт так:

script.pl < entry.txt

Сценарий работает независимо от того, сколько разделов ввода включено и как долго они разделены, если только заголовки разделов ввода похожи на [ some text ] .


Если вы предпочитаете нечитаемый код или просто не хотите хранить скрипт где-либо, вы можете использовать эту единственную команду:
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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .