1

我正在尝试拆分一个非常规则的巨大文本文件(约 5 亿行文本),如下所示:

-- Start ---

blah blah

-- End --

-- Start --

blah blah

-- End --

...

其中 ... 表示重复模式,“blah blah”的长度可变~ 2000 行。我想分开第一个

-- Start --

blah blah

-- End --

块到一个单独的文件中,并以最快的方式(运行时,考虑到我将运行多次)从原始文件中删除它。

理想的解决方案是从原始文件中剪切初始块并将其粘贴到新文件中,而不加载巨大的初始文件的尾部。

我通过以下方式尝试了 csplit:

csplit file.txt /End/+1 

这是一种有效的方法,但在时间上效率不高。

编辑:如果我们从文件中删除最后一个“start-end”块而不是第一个块,是否有解决方案?

4

3 回答 3

6

如果要从原始文件中删除开头,则别无选择,只能读取和写入文件的整个其余部分。要删除结尾(正如您在编辑中建议的那样),它可以更有效:

use File::ReadBackwards;
use File::Slurp 'write_file';
my $fh = File::ReadBackwards->new( 'inputfile', "-- End --\n" )
    or die "couldn't read inputfile: $!\n";
my $last_chunk = $fh->readline
    or die "file was empty\n";
my $position = $fh->tell;
$fh->close;
truncate( 'inputfile', $position );
write_file( 'lastchunk', $last_chunk );
于 2014-05-12T06:01:59.050 回答
2

也许像下面这样的东西会帮助你:

-- End --在每个标记后拆分文件。使用简单的递增后缀创建新文件。

use strict;
use warnings;
use autodie;

my $file = shift;

my $i = 0;
my $fh;

open my $infh, '<', $file;

while (<$infh>) {
    open $fh, '>', $file . '.' . ++$i if !$fh;
    print $fh $_;
    undef $fh if /^-- END --/;
}

不幸的是,没有truncate从文件开头删除数据的等效方法。

如果您真的想分阶段执行此操作,那么我建议您只是tell读取的最后一个位置,以便seek在您准备好输出另一个文件时可以这样做。

于 2014-05-12T05:18:06.330 回答
0

您可以使用flip-flop运算符来获取此模式之间的内容:

use File::Slurp;
my @text = read_file( 'filename' ) ;
foreach my $line (@text){
  if ($line =~ /Start/ .. /End/) {
    # do stuff with $line
    print $line; # or so
  }
}

当您的文件很大时,请小心一次吞下整个文件!

于 2014-05-12T05:11:41.690 回答