如何加速我的Perl正则表达式匹配?

时间:2009-10-20 03:48:13

标签: regex perl

我想使用以下正则表达式捕获多个文本:

$text_normal = qr{^(\/F\d+) FF (.*?) SCF SF (.*?) MV (\(.*?)SH$};

字符串的示例如下所示:

my $text = '/F12345 FF FF this is SCF SF really MV (important stuff SH';

可以重写以加快匹配吗?

5 个答案:

答案 0 :(得分:18)

优化正则表达式没有单一的答案。您可以使用re编译指示来查看特定正则表达式正在执行的操作:

 use re 'debugcolor';

一旦你看到它遍历字符串,你会看到它出现问题的位置并从那里调整你的正则表达式。当你这样做时,你将学习一些关于正则表达式引擎的知识。

您还应该查看Mastering Regular Expressions,它会告诉您正则表达式如何工作以及为什么某些模式比其他模式慢。

答案 1 :(得分:6)

这在很大程度上取决于您正在扫描的数据的配置文件。

你可以做的是识别你过滤掉最多输入的你的RE 为该表达式做一个单独的简单RE。

例如,如果只有5%的输入日期包含'MV'字符串, 你可以先过滤这个,然后只应用更复杂的RE 更简单的一个是真的

所以你会:

if ( $text_normal =~ / MV / ) {
  $text_normal = qr{^(\/F\d+) FF (.*?) SCF SF (.*?) MV (\(.*?)SH$};
  if .......
  }
}

答案 2 :(得分:6)

没有看到一些样本数据,很难说。

通常,避免使用.*是个好主意。寻找任何可能的不需要的回溯来源,并消除它们。

如果您的需求很简单,您可以使用切片split离开。

 my @vals = (split / /, $string)[0,2,5,7];

答案 3 :(得分:2)

(.*)意味着你在处理任何数量的“SCF SF”重复之前,你会发现它表明它是下一个捕获的那个,通过使它非贪婪,你仍然在处理甚至是'SCF SF'将出现在'FF'之后的捕获中。我认为你正在处理很多你不需要的案件。

优化正则表达式的最佳方法有时会使其更加神秘 - 但您肯定会找到使表达式早期失败的方法(.*?)而不是“贪婪”绝对是宽容。

下面是第二次捕获的更详细但更快失败的替代方法。

((?:[^S]|S[^C]|SC[^F]|SCF[^ ]|SCF [^S]|SCF S[^F])*)

但是如果您认为字符串\bSCF\b应该自动进行捕获提交并且只期望“\ bSCF SF \ b”,那么您可以进一步优化它。因此,您可以将其重写为:

((?:[^S]|S[^C]|SC[^F]SCF\B)*) SCF SF

但是你可以通过回溯控制来优化这些字符串。如果你认为世界上没有办法让SCF作为一个单词出现,并且在有效输入时不会被SF跟随。为此,您可以使用方括号(?>)添加另一个组。

(?>((?:[^S]|S[^C]|SC[^F]SCF\B)*)) SCF SF

这意味着匹配逻辑绝不会尝试重新评估它捕获的内容。如果此后的字符不能成为“SCF SF”,则整个表达式失败。并且在它试图容纳“MV”和其他子表达式之前很久就失败了。

事实上,鉴于有关分隔符唯一性的某些表达式,此表达式的最快性能将是:

$text_normal = qr{^(\/F\d+) FF (?>((?:[^S]|S[^C]|SC[^F]SCF\B)*))SCF SF (?>((?:[^M]|M[^V]|MV\B)*))MV (?>(\((?:[^S]|S[^H]|SH.)*))SH$};

此外,详细的,详尽的否定匹配可以替代表达否定的前瞻 - 但我不知道它如何在性能上起作用。但负面展望会像这样:

((?:.(?! SCF))*) SCF SF

这意味着对于此捕获,我想要任何不是以字符串“SCF SF”开头的空格的字符。

答案 4 :(得分:0)

回溯是杀死正则表达式性能最可靠的方法之一,但不幸的是,这似乎并不是你能够完全消除.通配符以支持字符类的情况,除非您捕获的文本禁止包含大写字符。 (如果该禁令确实存在,您可以替换.*?代替[a-z ]*。)通过使用{}设置最小/最大数量,您仍然可以减少回溯的可能性。要匹配的字符,例如.{0,10}?,如果匹配不能超过10个字符。