Perl正则表达式:我如何在不将整个文件读入内存的情况下搜索文件中的多行模式?

时间:2017-03-22 16:32:02

标签: regex perl

我的问题与How do I re.search or re.match on a whole file without reading it all into memory?的问题相同,但使用的是perl而不是python

问题: 我希望能够在整个文件上运行正则表达式,但我希望能够不必立即将整个文件读入内存,因为我可能正在使用相当大的文件未来。有没有办法做到这一点?谢谢!

澄清:我无法逐行阅读,因为它可以跨越多行。

为什么我使用perl而不是python?我已经遇到了python正则表达式的足够问题,我需要切换到perl。我会安装https://pypi.python.org/pypi/regex,但我不能,因为我的工作场所,可以理解,不允许写入其python安装目录,我宁愿避免使用IT来实现缓慢的来回电子邮件链他们为我安装,和/或处理进一步的许可问题:)

编辑:示例模式我正在寻找

assign signal0 = (cond1) ? val1 :
                 (cond2) ? val2 :
                           val3;

assign signal1[15:0] = {input1[7:0], input2[7:0]};

assign signal2[34:0] = { 4'b0,
                         subsig0[3:0],
                         subsig1,
                         subsig2,
                         subsig3[18:2],
                         subsig4[5:0]
                       };

我正在寻找类似上面的模式,即在看到分号之前进行变量分配。正则表达式将与上述任何一个匹配,因为我不知道该模式是否是多行的。也许类似于/assign\s+\w+\s+=\s+[^;];/m,即直到我看到分号

EDIT2:从给定的答案(谢谢!)看来,将模式分解为开始,中间和&结束部分可能是最佳策略,例如根据一些人的建议使用范围运算符。

4 个答案:

答案 0 :(得分:4)

您可以使用range operator来匹配两个模式之间的所有内容,同时逐行阅读:

use strict;
use warnings 'all';

while (<DATA>) {
    print if /^assign / .. /;/;
}

__DATA__
foo
assign signal0 = (cond1) ? val1 :
                 (cond2) ? val2 :
                           val3;
bar
assign signal1[15:0] = {input1[7:0], input2[7:0]};
baz
assign signal2[34:0] = { 4'b0,
                         subsig0[3:0],
                         subsig1,
                         subsig2,
                         subsig3[18:2],
                         subsig4[5:0]
                       };
qux

输出:

assign signal0 = (cond1) ? val1 :
                 (cond2) ? val2 :
                           val3;
assign signal1[15:0] = {input1[7:0], input2[7:0]};
assign signal2[34:0] = { 4'b0,
                         subsig0[3:0],
                         subsig1,
                         subsig2,
                         subsig3[18:2],
                         subsig4[5:0]
                       };

答案 1 :(得分:3)

您可以将输入记录分隔符$/设置为分号;并逐行读取。每行都将在声明中,包括尾随分号。然后匹配变得微不足道。

答案 2 :(得分:2)

我可以想象两种解决方案(没有大量思考,所以也许我错了):

a)使用最大匹配字符数,例如1024. 1)读入两倍(2048)个字符。 2)尝试匹配。 3)向前搜索1024个字符。重复。

b)使用匹配在单行中的起始和结束模式。之间的部分可以在以后测试。 Perl的触发器操作符可以在这种情况下使用。

编辑:由于问题得到了更新,解决方案b)似乎很好。

起始模式是赋值,结束模式是分号。中间的所有内容都可以连接在一起,然后进行有效性测试。

示例:

my $assignment = "";
while (<>) {
    if (/assign\s+\w+\s+=/ .. /;/) {
        $assignment .= $_;
    } else {
        if ($assignment =~ /full regex/) {
            # do something with the match
        }
        $assignment = "";
    }
}

答案 3 :(得分:1)

以下是使用渐进式匹配和预匹配模式的示例:

use feature qw(say);
use strict;
use warnings;

my $pre_match = qr{assign\s+\S+\s+=\s+};
my $regex = qr{($pre_match[^;]+;)};

my $line = "";
my $found_start = 0;
while( <DATA> ) {
    if ( !$found_start && /$pre_match/ ) {
        $line = "";
        $found_start = 1;
    }
    if ( $found_start ) {
        $line .= $_;
        if ( $line =~ /$regex/ ) {
            say "Got match: '$1'";
            $found_start = 0;
            $_ = substr $line, $+[0];
            redo;
        }
    }
}

__DATA__
assign signal0 = (cond1) ? val1 :
                 (cond2) ? val2 :
                           val3;

assign signal1[15:0] = {input1[7:0], input2[7:0]};

assign signal2[34:0] = { 4'b0,
                         subsig0[3:0],
                         subsig1,
                         subsig2,
                         subsig3[18:2],
                         subsig4[5:0]
                       };

<强>输出

Got match: 'assign signal0 = (cond1) ? val1 :
                 (cond2) ? val2 :
                           val3;'
Got match: 'assign signal1[15:0] = {input1[7:0], input2[7:0]};'
Got match: 'assign signal2[34:0] = { 4'b0,
                         subsig0[3:0],
                         subsig1,
                         subsig2,
                         subsig3[18:2],
                         subsig4[5:0]
                       };'