Perl,有效地使用正则表达式擦除文件的问题

时间:2014-03-20 03:19:18

标签: regex arrays perl

首先,我将解释我的程序试图解决的问题。我有两个输入文件,一个包含" good"数:

100000
100001
100002
100003
100004

另一个文件是" raw"我希望检查每一行的数字,看看该行是否包含其中一个" good"上面的数字后跟4个数字,另外4个数字可以是任何数字。所以如果包含原始数字的文件是:

8881000001234
1000014321
999991000021234567
00234100001
1000041234
100002123
1000029876

用正则表达式擦除后,匹配的数字将是

1000001234
1000014321
1000021234
1000041234
1000029876

到目前为止,我这样做的方法是存储好的"数组中的数字然后啜饮了#34; raw"数字变成标量

my $FH
my@good_nums
open $FH, '<', 'good_numbers' or die $!;
while(<$FH>) { chomp; push @good_nums, $_; }
close $FH;

open $FH, '<', 'raw_numbers' or die $!;
my $raw_nums = do { local $/; <$FH> };
close $FH;

然后我可以这样做:

my @matches;
foreach my $num (@good_nums) {
    push @matches, $raw_nums =~ /$num\d{4}/g;
}

所以@matches包含正确的匹配,这一直运作良好。

但是现在我已经开发出了捕捉&#34; raw&#34;数字不匹配。我可以通过放置&#34; raw&#34;来捕获不匹配的行。将数字放入一个数组(而不是啜饮它们)并将join数组@good_nums放入一个正则表达式中:

my $QRnums = '(?:' . (join '|', @good_nums) . ')';
$QRnums = qr/$QRnums/;

my @raw_nums;
open my $FH, '<', 'raw_numbers' or die $!;
while(<$FH>) { chomp; push @raw_nums, $_; }

my @matches;
my @junk;
for (@raw_nums) {   
    if ($_ =~ /($QRnums\d{4})/g) {
        push @matches, $1;
    } else {
        push @junk, $_;
    }
}

这是有效的,但是当我将每个文件中的行数增加到150,000或更多时,后一种解决方案比前一种解决方案长4或5倍。我知道必须有另一个可以有效解决我的问题的Perl解决方案,但我不知所措。我对中间Perl及其他方面都不是很好..有没有更好的方法来做这个或者我的第一个解决方案可以重写,以便我也可以获得数组中的非匹配?除了需要解决我的帖子开头解释的问题,我对任何事情持开放态度。

1 个答案:

答案 0 :(得分:1)

仅缓存正则表达式一次,然后使用$_ =~ $QRnums进行比较。

此外,无需篡改您的其他文件,只需逐行处理。

my $QRnums = '(?:' . (join '|', @good_nums) . ')';
$QRnums = qr/($QRnums\d{4})/;

my @matches;
my @junk;

open my $FH, '<', 'raw_numbers' or die $!;
while (<$FH>) {
    chomp;
    if ($_ =~ $QRnums) {
        push @matches, $1;
    } else {
        push @junk, $_;
    }
}

此外,如果您的正则表达式应该绑定到字符串^的开头,那么我建议您添加:$QRnums = qr/^($QRnums\d{4})/;

<强>附录

来自perlop - Regexp Quote-Like Operators

  

由于Perl可以在执行qr()运算符时编译模式,因此在某些情况下使用qr()可能具有速度优势,特别是如果qr()的结果是独立使用的话:

以后:

  

qr()时刻将模式预编译为内部表示,避免了每次尝试匹配/$pat/时都需要重新编译模式。 (Perl有许多其他内部优化,但如果我们不使用qr()运算符,则不会在上面的例子中触发。)

基本上,因为你的@good_nums列表可能非常大,所以如果我们可以这样,那么只需要编译一次正则表达式测试就可以缓存它。