【问题标题】:split file on Nth occurrence of delimiter在第 N 次出现分隔符时拆分文件
【发布时间】:2013-03-11 16:45:01
【问题描述】:

在分隔符的每 N 次出现 之后,是否有一种单行方法可以将文本文件拆分成片段/块?

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

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

有几百万个条目,因此每次出现分隔符“+”时都进行拆分是个坏主意。例如,我想拆分分隔符“+”的每 50,000 个实例。

Unix 命令“split”和“csplit”似乎不这样做......

【问题讨论】:

    标签: file unix split chunking


    【解决方案1】:

    使用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 停止处理这条记录的规则并前进到下一个(行)。我还将&gt;&gt; 更改为&gt;,因为如果您多次运行它,您可能不想附加旧的块文件。

    【讨论】:

    • 但这会单独附加每一行...。由于 i/o 太多,这不会非常慢吗?
    • 来自 gawk 手册“使用 &gt;', >> 重定向输出”或“|”仅当您指定的特定文件或命令尚未被您的程序写入,或者自上次写入后已关闭时,才要求系统打开文件或管道。”所以这与在 shell 中做有点不同。
    • 哇,这是非常技术性的问题。但是很有用!
    • 最后一个关于奖励积分的问题 - 使用这种方法,创建的每个“块”文件的第一行是上面的分隔符 +)。如果我不希望每个文件的第一行或最后一行作为分隔符怎么办? (即“干净地”开始和结束)。
    【解决方案2】:

    在简洁的“单行”中使用+ 作为输入分隔符

    如果你想做$_ &gt; 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 $_ &gt; newprefix.part.$c 对吧?
    • doSomethingWith() 可以是你想要对每个块做的任何事情,所以是的。你想要这样吗?
    【解决方案3】:

    如果你找不到合适的替代品,在 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
    +
    $
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-29
      • 1970-01-01
      • 2019-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-08
      相关资源
      最近更新 更多