按照分隔符的第N个出现位置拆分文件

14

有没有一行代码可以在每个分隔符的Nth次出现后将文本文件拆分成片段/块?

例如:下面的分隔符是“+”

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

由于有数百万个条目,因此在每次出现分隔符“+”时拆分是一个不好的想法。我想要在第50,000个分隔符“+”的实例上进行拆分。

Unix命令“split”和“csplit”似乎都无法做到这一点...

3个回答

14

使用awk,您可以:

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

更新:

如果不想包含分隔符,请尝试以下方法:

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

next关键字会使awk停止处理当前记录并跳到下一行。我还将>>更改为>,因为如果您运行它多次,您可能不想追加旧的块文件。


但这样会逐行追加... 这不会因为太多的输入输出而变得非常慢吗? - cmo
2
从gawk手册中可以了解到,“使用>', >>'或`|'重定向输出会请求系统仅在程序还没有写入特定文件或命令,或者自上次写入之后该文件已关闭时打开一个文件或管道。”因此,它与在shell中的操作略有不同。 - FatalError
哇,那真是非常技术性的捕捉。但很有用! - cmo
最后一个奖励问题 - 使用这种方法,每个“块”文件中的第一行是分隔符+。如果我既不想要每个文件的第一行也不想要最后一行作为分隔符怎么办?(即,干净地开始和结束)。 - cmo

1

如果找不到合适的替代方案(而且它的性能还不错),在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
+
$

0

使用+作为简洁的“一行代码”中的输入分隔符

如果您想像您在评论中所述的那样执行$_ > 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.*

文档


"doSomethingWith" 应该类似于 cat $_ > newprefix.part.$c 对吗? - cmo
doSomethingWith() 可以是你想要对每个块执行的任何操作,所以是的。你想这样吗? - Gilles Quénot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接