首先,我将解释我的程序试图解决的问题。我有两个输入文件,一个包含" 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及其他方面都不是很好..有没有更好的方法来做这个或者我的第一个解决方案可以重写,以便我也可以获得数组中的非匹配?除了需要解决我的帖子开头解释的问题,我对任何事情持开放态度。
答案 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
列表可能非常大,所以如果我们可以这样,那么只需要编译一次正则表达式测试就可以缓存它。