【问题标题】:Perl, problems scrubbing a file with regex effecientlyPerl,使用正则表达式有效地擦洗文件的问题
【发布时间】:2014-03-21 08:06:08
【问题描述】:

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

100000
100001
100002
100003
100004

另一个文件是一个“原始”数字文件,我想检查每一行,看看该行是否包含上面的“好”数字之一,后跟 4 个数字,另外 4 个数字可以是任何数字。所以如果包含原始数字的文件是:

8881000001234
1000014321
999991000021234567
00234100001
1000041234
100002123
1000029876

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

1000001234
1000014321
1000021234
1000041234
1000029876

到目前为止,我这样做的方法是将“好的”数字存储在一个数组中,然后将“原始”数字转换成一个标量

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 包含正确的匹配项,并且运行良好。

但现在我已经开发了从不匹配的“原始”数字中捕获行的需要。我可以通过将“原始”数字放入一个数组(而不是吞下它们)并将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 及更高版本。. 有没有更好的方法来做到这一点,或者我的第一个解决方案可以重写,以便我也可以获得数组中的非匹配项?除了需要解决我的帖子开头所解释的问题之外,我对任何事情都持开放态度。

【问题讨论】:

    标签: regex arrays perl


    【解决方案1】:

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

    此外,无需对其他文件进行 slurp,只需逐行处理即可。

    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 列表可能非常大,所以如果可以的话,缓存它是有意义的,这样正则表达式测试只需要编译一次。

    【讨论】:

    • 谢谢,这是一个很大的帮助,并且比我所做的更有效,如果你能向我解释缓存如何与正则表达式一起工作,以及我的示例如何处理缓存,这对我来说将是一个巨大的帮助.如果这个话题太宽泛,你能指出我有什么好的参考吗?再次感谢。
    • 添加了关于缓存正则表达式文档的链接。
    猜你喜欢
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-26
    • 1970-01-01
    相关资源
    最近更新 更多