Разделить файл на N-е вхождение разделителя

Есть ли однострочный разделитель текстового файла на куски/куски после каждого N-го вхождения разделителя?

пример: разделителем ниже является "+"

entry 1
some more
+
entry 2
some more
even more
+
entry 3
some more
+
entry 4
some more
+
...

Есть несколько миллионов записей, поэтому расщепление на каждом появлении разделителя "+" - плохая идея. Я хочу разделить, скажем, каждый 50 000-й экземпляр разделителя "+" .

Unix-команды "split" и "csplit" просто не делают этого...

Ответ 1

Используя awk, вы можете:

awk '/^\+$/ { delim++ } { file = sprintf("chunk%s.txt", int(delim / 50000)); print >> file; }' < input.txt 

Update:

Чтобы не включать разделитель, попробуйте следующее:

awk '/^\+$/ { if(++delim % 50000 == 0) { next } } { file = sprintf("chunk%s.txt", int(delim / 50000)); print > file; }' < input.txt 

Ключевое слово next вызывает awk, чтобы остановить правила обработки этой записи и перейти к следующей (строке). Я также изменил >> на >, поскольку, если вы запускаете его более одного раза, вы, вероятно, не хотите добавлять старые файлы chunk.

Ответ 2

Это не очень сложно сделать в Perl, если вы не можете найти подходящую альтернативу (и она будет работать очень хорошо):

#!/usr/bin/env perl
use strict;
use warnings;

# Configuration items - could be set by argument handling
my $prefix = "rs.";     # File prefix
my $number = 1;         # First file number
my $width  = 4;         # Number of digits to use in file name
my $rx     = qr/^\+$/;  # Match regex
my $limit  = 3;         # 50,000 in real case
my $quiet  = 0;         # Set to 1 to suppress file names

sub next_file
{
    my $name = sprintf("%s%.*d", $prefix, $width, $number++);
    open my $fh, '>', $name or die "Failed to open $name for writing";
    print "$name\n" unless $quiet;
    return $fh;
}

my $fh = next_file;  # Output file handle
my $counter = 0;     # Match counter
while (<>)
{
    print $fh $_;
    $counter++ if (m/$rx/);
    if ($counter >= $limit)
    {
        close $fh;
        $fh = next_file;
        $counter = 0;
    }
}
close $fh;

Это далеко не однострочный; Я не уверен, что это заслуга или нет. Элементы, которые должны быть сконфигурированы, группируются вместе и могут быть установлены, например, с помощью параметров командной строки. У вас может быть пустой файл; вы можете определить это и удалить, если это необходимо. Вам понадобится второй счетчик; существующий - это счетчик совпадений, но вам также нужен счетчик строк, и если счетчик строк был равен нулю, вы удалили бы последний файл. Вам также понадобится имя, чтобы удалить его... неудобно, но не сложно.

Дайте вход (в основном две копии ваших данных образца), вывод из repsplit.pl (повторный раскол) был таким, как показано:

$ perl repsplit.pl data
rs.0001
rs.0002
rs.0003
$ cat data
entry 1
some more
+
entry 2
some more
even more
+
entry 3
some more
+
entry 4
some more
+
entry 1
some more
+
entry 2
some more
even more
+
entry 3
some more
+
entry 4
some more
+
$ cat rs.0001
entry 1
some more
+
entry 2
some more
even more
+
entry 3
some more
+
$ cat rs.0002
entry 4
some more
+
entry 1
some more
+
entry 2
some more
even more
+
$ cat rs.0003
entry 3
some more
+
entry 4
some more
+
$

Ответ 3

Использование и + как разделитель входных данных в сжатом "однострочном":

Если вы хотите сделать $_ > newprefix.part.$c, как указано в комментарии:

$ limit=50000 perl -053 -Mautodie -lne '
    BEGIN{$\=""}
    $count++;
    if ($count >= $ENV{limit}) {
        open my $fh, ">", "newprefix.part.$c";
        print $fh $_;
        close $fh;
    }
' file.txt

$ ls -l newprefix.part.*

Doc