我想使用以下正则表达式捕获多个文本:
$text_normal = qr{^(\/F\d+) FF (.*?) SCF SF (.*?) MV (\(.*?)SH$};
字符串的示例如下所示:
my $text = '/F12345 FF FF this is SCF SF really MV (important stuff SH';
可以重写以加快匹配吗?
答案 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个字符。