Perl读取一个大文件,用于多行正则表达式

时间:2017-03-17 20:32:49

标签: regex perl large-files

我有一个4GB的文本文件,长度线变化很大,这只是一个示例文件,生产文件会大得多。我需要读取文件并应用多行正则表达式。

为多行正则表达式读取这么大的文件的最佳方法是什么?

如果我逐行阅读,我认为我的多行正则表达式无法正常工作。当我在3参数形式中使用read函数时,我的正则表达式结果随着我在read语句中指定的长度大小而变化。我相信文件的大小太大而无法读入数组或内存。

这是我的代码

package main;
use strict;
use warnings;

our $VERSION = 1.01;
my $buffer;
my $INFILE;
my $OUTFILE;

open $INFILE, '<', ... or die "Bad Input File: $!";
open $OUTFILE, '>',... or die "Bad Output File: $!";

while ( read $INFILE, $buffer, 512  ) {
    if ($buffer =~ /(?m)(^[^\r\n]*\R+){1}^(B|BREAK|C|CLOSE|D|DO(?! NOT)|E|ELSE|F|FOR|G|GOTO|H|HALT|HANG|I|IF|J|JOB|K|KILL|L|LOCK|M|MERGE|N|O|OPEN|Q|QUIT|R|READ|S|SET|TC|TRE|TRO|TS|U|USE|V|VIEW|W|WRITE|X|XECUTE)( |:).*[^\r\n]/) {
        print $OUTFILE $&;
        print $OUTFILE "\n";
    }
}

close( $INFILE ); 
close( $OUTFILE );
1;

以下是一些示例数据:

^%Z("EUD")
S %L=%LO,%N="E1"
^%Z("RT")
This is data that I don't want the regex to find
^%Z("EXY")
X ^%Z("EW2"),^%Z("ELONG"):$L(%L)>245 S %N="E1" Q:$L(%L)>255  X ^%ZOSF("EON") S DX=0,DY=%EY,X=%RM+1 X ^%ZOSF("RM"),XY K %EX,%EY,%E1,%E2,DX,DY,%N Q
^%Z("F12")
S %A=$P(^DIC(9.8,0),"^",3)+1,%C=$P(^(0),"^",4)+1 X "F %=0:0 Q:'$D(^DIC(9.8,%A,0))  S %A=%A+1" S $P(^DIC(9.8,0),"^",3,4)=%A_"^"_%C,^DIC(9.8,%A,0)=%X_"^R",^DIC(9.8,"B",%X,%A)=""
^%Z("F2")
S %=$H>21549+$H-.1,%Y=%\365.25+141,%=%#365.25\1,%D=%+306#(%Y#4=0+365)#153#61#31+1,%M=%-%D\29+1,%DT=%Y_"00"+%M_"00"+%D,%D=%M_"/"_%D_"/"_$E(%Y,2,3)

上面的行在语法上是成对的(第1行和第2行合在一起,第3行和第4行等)。我需要在上面的数据中找到特定的对,除了:

之外的所有对
^%Z("RT")
This is data that I don't want the regex to find

1 个答案:

答案 0 :(得分:2)

显然关于解析DSL的问题,似乎一般来说正则表达式不是正确的工具。除pages of CPAN modulesthis article之类的帖子外,快速搜索不会产生一个简单的已接受方法列表。找出最佳方法确实是第一步。

但是,下面是标题和清晰描述中所述问题的答案:如何解析一个非常大的文件,其中要处理的单位分布在未知行数上。

继续组装“缓冲区”并进行检查。找到匹配后,处理并清除它。

例如,在变量上加一行并检查(如果使用正则表达式,请尝试匹配)。继续前进,一旦匹配过程并清除变量。

my $unit;
while (<$fh>) {
    # chomp;     # if suitable
    $unit .= $_;

    if ( test_unit($unit) ) {
         # process ...
         $unit = undef;
    }
}

test_unit是代码的占位符,用于决定是否应该处理组合的单元。如果是正则表达式,则可以在循环之前定义my $re = qr/.../;(请参阅qr in perlop),然后使用if ($unit =~ $re)

在循环中进行测试

问题中的注释表明要处理的行成对出现,但在注释中澄清了后续行并不总是成对的。因此,我们无法处理线对。