假设您要匹配由双字符分隔的文本,如下所示:
a = <<
Hello
World!
>>
正则表达式/<<(.*)>>/似乎会这样做,但不幸的是,当这些可以重复时,贪婪的匹配变得太多了:
a = <<
Hello
World!
>>
b = <<
Goodbye
World!
>>
之前的正则表达式将捕获
Hello
World!
>>
b = <<
Goodbye
World!
显而易见的答案是让regexp非贪婪:/&lt;&lt;(。*?)&gt;&gt; /
不幸的是,这对于长字符串(至少在Perl中)存在极端的性能问题。如果分隔符是单个字符,那么我们可以使用字符类(除了字符之外的所有字符)来解决贪婪问题。
关于正则表达式的任何想法,以使这种匹配没有性能损失?
注意:我必须使用Perl,这必须是一个正则表达式,因为它嵌入了更大的系统。
感谢。
答案 0 :(得分:4)
扩展drewk的答案,以便它确实有效:
/<<((?:(?>[^>]+)|>(?!>))*)>>/
匹配“&lt;&lt;”,然后是0个或更多个块的序列,这些块是任意数量的非“&gt;”字符或单个“&gt;”没有跟着另一个“&gt;”,最后是“&gt;&gt;”。
答案 1 :(得分:3)
您使用的是Perl 5.10吗?试试这个:
/<<([^>]*+(?:>(?!>)[^>]*+)*+)>>/
正如@hobbs发布的正则表达式一样,只有在找到>
后才能执行前瞻(与非贪婪量词相反,后者在每个位置都有效地进行前瞻)。但是这个使用了Friedl的“展开循环”技术,它应该比交替方法稍快一些。此外,所有量词都具有占有性,因此不需要保存可能使回溯成为可能的状态信息。
答案 2 :(得分:2)
在这种情况下使用否定字符类将起作用:
/<<([^>]*)>>/
与/<<(.*)>>/
具有相同的探测次数,因此与/<<(.*?)>>/
我同意DVK;是正则表达式唯一的方法吗?
答案 3 :(得分:1)
在这种情况下,请查看专用解析器(例如Text::Balanced)的性能是否可以接受。它不是正则表达式,但如果没有关于“NB”poststcriptum的更多详细信息,那么在寻找仅使用正则表达式的解决方案时,您可能会有XY problem。
如果您绝对必须使用正则表达式,请查看使用前瞻功能 - 它可以提高速度。
答案 4 :(得分:1)
假设你有一个简单的语法
my $p = Parse::RecDescent->new(<<'EOGrammar');
program: assignment(s)
assignment: id '=' '<<' angle_text '>>'
{ $return = [ $item{id}, $item{angle_text} ] }
angle_text: <skip:undef> / ( [^>] | >(?!>) )* /x
id: /\w+/
EOGrammar
和
的源文本a = <<
Hello
World!
>>
b = <<
Goodbye
World!
>>
使用
处理结果时for (@{ $p->program($text) }) {
my($name,$what) = @$_;
print "$name: [[[$what]]]\n";
}
你会看到
的输出a: [[[ Hello World! ]]] b: [[[ Goodbye World! ]]]