【问题标题】:What's the best way to apply many Perl regular expression tests?应用许多 Perl 正则表达式测试的最佳方法是什么?
【发布时间】:2011-04-29 11:38:47
【问题描述】:

我有一个 Perl 模块,可以将文本与数百个正则表达式列表进行匹配;目前我只是在或他们:

if (
  /?:re1/ or
  ...
  /re200$/
) { return "blah"; }

有没有更好/更快/更少资源密集型的方法来做到这一点?也许存在一个有用的模块,或者我应该将它们存储在哈希等中。

【问题讨论】:

标签: regex perl


【解决方案1】:

看看Regexp::Assemble

这是来自描述:

Regexp::Assemble 采用任意数量的正则表达式并将它们组合成一个正则表达式(或 RE),该正则表达式匹配所有单个 RE 匹配的内容。

因此,无需循环使用大量表达式,只需针对一个表达式测试目标字符串即可。当您有数千种模式要处理时,这很有趣。努力产生尽可能小的图案。

还可以跟踪原始模式,以便您可以确定在形成组合模式的源模式中,哪个是导致匹配发生的模式。

我将它用于一些项目,效果非常棒。

【讨论】:

  • 我使用 Regexp::Assemble 从澳大利亚地址列表中提取城镇名称。根据官方数据源,其中大约有 10,000 个,而我提取城镇的代码速度之快令人难以置信。
  • +1:你打败了我。我正在使用 Regexp::Assemble 一次匹配 1700 个左右的术语,它的工作原理很棒
【解决方案2】:

来自perlfaq6How do I efficiently match many regular expressions at once?的回复


如何一次有效地匹配多个正则表达式?

(由布赖恩·德·福伊提供)

如果您有 Perl 5.10 或更高版本,这几乎是微不足道的。您只需智能匹配一组正则表达式对象:

my @patterns = ( qr/Fr.d/, qr/B.rn.y/, qr/W.lm./ );

if( $string ~~ @patterns ) {
    ...
    };

智能匹配在找到匹配项时停止,因此不必尝试每个表达式。

在 Perl 5.10 之前,您需要做一些工作。您希望避免每次要匹配正则表达式时都编译它。在这个例子中,perl 必须为 foreach 循环的每次迭代重新编译正则表达式,因为它无法知道 $pattern 将是什么:

my @patterns = qw( foo bar baz );

LINE: while( <DATA> ) {
    foreach $pattern ( @patterns ) {
        if( /\b$pattern\b/i ) {
            print;
            next LINE;
            }
        }
    }

qr// 运算符出现在 perl 5.005 中。它编译正则表达式,但不应用它。当您使用正则表达式的预编译版本时,perl 的工作量会减少。在此示例中,我插入了一个映射以将每个模式转换为其预编译形式。脚本的其余部分相同,但速度更快:

my @patterns = map { qr/\b$_\b/i } qw( foo bar baz );

LINE: while( <> ) {
    foreach $pattern ( @patterns ) {
        if( /$pattern/ )
            {
            print;
            next LINE;
            }
        }
    }

在某些情况下,您可以将多个模式组合成一个正则表达式。但请注意需要回溯的情况。

my $regex = join '|', qw( foo bar baz );

LINE: while( <> ) {
    print if /\b(?:$regex)\b/i;
    }

有关正则表达式效率的更多详细信息,请参阅 Jeffrey Freidl 的 Mastering Regular Expressions。他解释了正则表达式引擎是如何工作的,以及为什么有些模式出奇地低效。一旦您了解 perl 如何应用正则表达式,您就可以针对具体情况调整它们。

【讨论】:

  • 我应该因为在 perl 文档中没有看到这一点而被扣分。顺便说一句,我并没有忘记您包含“贡献者”的重要性。感谢您的帮助!
【解决方案3】:

您可以将正则表达式保存在单独的文件中,每行一个。它不会运行得更快,但代码会更干净。

用类似的东西阅读它们

my @patterns;
while (<>) {
    push @patterns, qr/$_/;
}

为了提高性能,我唯一的建议是使用真正的解析器。 ANTLR 的 perl 目标似乎已失效,但我使用通过 perl 的 Inline::Java 生成的 Java 解析器没有问题。

编辑:确实想到了另一件小事:将您的正则表达式从最常见匹配到最不常见匹配。可以给你一个小的常数因子改进。

【讨论】:

    【解决方案4】:

    如果你的正则表达式没有改变,那么你应该使用 /o 后缀,这将要求它们只编译一次。这对速度有很大帮助。

    此外,按“最常见的拒绝优先”或“通配符最少的优先”对它们进行排序,以便后面的那些 CPU 密集型的执行频率最低。

    当然可以从文件中读取它们,但是为了提高速度,正确的方法是读取它们,然后在文本字符串中构造一个子程序,然后对文本字符串求值以返回一个匿名子程序来调用.这为您提供了硬编码正则表达式列表的速度优势以及正则表达式可变列表的灵活性。

    【讨论】:

    • qr//方便多了,子代是5.5.3之前的。
    猜你喜欢
    • 1970-01-01
    • 2016-05-18
    • 1970-01-01
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    相关资源
    最近更新 更多