正则expression式可以用来匹配嵌套模式吗?
是否可以编写一个正则expression式来匹配发生未知次数的嵌套模式? 例如,如果在外括号内嵌套了未知数量的打开/closures大括号,则正则expression式是否可以匹配打开和closures大括号?
例如:
public MyMethod() { if (test) { // More { } } // More { } } // End
应该匹配:
{ if (test) { // More { } } // More { } }
不,那很简单。 有限自动机(它是正则expression式下面的数据结构)除了它所处的状态之外没有内存,如果有任意深度的嵌套,则需要一个任意大的自动机,它与有穷自动机的概念相冲突。
您可以将嵌套/配对的元素匹配到固定的深度,深度仅受内存限制,因为自动机变得非常大。 然而,在实践中,你应该使用一个下推自动机,即一个上下文无关文法的parsing器,例如LL(自上而下)或LR(自下而上)。 你必须考虑更糟的运行时间行为:O(n ^ 3)与O(n),其中n =长度(input)。
有许多可用的parsing器生成器,例如Java的ANTLR 。 为Java(或C)find现有的语法也并不困难。
更多背景:维基百科的自动机理论
可能的工作Perl的解决scheme,如果string是在一行:
my $NesteD ; $NesteD = qr/ \{( [^{}] | (??{ $NesteD }) )* \} /x ; if ( $Stringy =~ m/\b( \w+$NesteD )/x ) { print "Found: $1\n" ; }
HTH
编辑:检查:
- http://dev.perl.org/perl6/rfc/145.html
- ruby资料: http : //www.ruby-forum.com/topic/112084
- 更多perl: http ://www.perlmonks.org/?node_id= 660316
- 甚至更多的Perl: https ://metacpan.org/pod/Text:: Balanced
- perl,perl,perl: http : //perl.plover.com/yak/regex/samples/slide083.html
还有一件事Torsten Marek (他指出正确,它不是正则expression式):
使用正则expression式来检查嵌套的模式是非常容易的。
'/(\((?>[^()]+|(?1))*\))/'
是的,如果它是.NET正则expression式引擎。 .Net引擎支持有外部堆栈的有限状态机。 见细节
正规语言的抽象引理是你不能这样做的原因。
生成的自动机将具有有限数量的状态,比如说k,所以一串k + 1个花括号必然会有一个重复的状态(如自动机处理字符)。 在同一状态之间的部分string可以被无限多次地复制,自动机将不知道这个差异。
特别是,如果它接受k + 1个开启支撑,然后是k + 1个紧接支撑(它应该),它也会接受抽出的开启支撑数,然后是不变的k + 1个closures支撑(不应该)。
适当的正则expression式将无法做到这一点,因为您将离开正规语言的领域降落在上下文无关语言领土。
尽pipe如此,许多语言提供的“正则expression式”包却更加强大。
例如, Lua正则expression式具有匹配平衡括号的“ %b()
”识别器。 在你的情况下,你会使用“ %b{}
”
另一个类似于sed的复杂工具是gema ,在这里你可以很容易地用{#}
来匹配平衡的花括号。
所以,根据您所掌握的工具,您的“正则expression式”(从更广泛的意义上来说)可以匹配嵌套括号。
在PHP正则expression式引擎中使用recursion匹配比括号的过程匹配要快得多。 尤其是在较长的弦乐中
http://php.net/manual/en/regexp.reference.recursive.php
例如
$patt = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!x'; preg_match_all( $patt, $str, $m );
与
matchBrackets( $str ); function matchBrackets ( $str, $offset = 0 ) { $matches = array(); list( $opener, $closer ) = array( '(', ')' ); // Return early if there's no match if ( false === ( $first_offset = strpos( $str, $opener, $offset ) ) ) { return $matches; } // Step through the string one character at a time storing offsets $paren_score = -1; $inside_paren = false; $match_start = 0; $offsets = array(); for ( $index = $first_offset; $index < strlen( $str ); $index++ ) { $char = $str[ $index ]; if ( $opener === $char ) { if ( ! $inside_paren ) { $paren_score = 1; $match_start = $index; } else { $paren_score++; } $inside_paren = true; } elseif ( $closer === $char ) { $paren_score--; } if ( 0 === $paren_score ) { $inside_paren = false; $paren_score = -1; $offsets[] = array( $match_start, $index + 1 ); } } while ( $offset = array_shift( $offsets ) ) { list( $start, $finish ) = $offset; $match = substr( $str, $start, $finish - $start ); $matches[] = $match; } return $matches; }
正如zsolt提到的,一些正则expression式引擎支持recursion – 当然,这些通常是那些使用回溯algorithm,所以它不会特别有效。 例如: /(?>[^{}]*){(?>[^{}]*)(?R)*(?>[^{}]*)}/sm
不,您正在进入无关语法语法领域。
这似乎工作:/( /(\{(?:\{.*\}|[^\{])*\})/m
: /(\{(?:\{.*\}|[^\{])*\})/m
我的问题+答案是相关的,我做了一个expression式和元expression式,可以匹配任意(有限)的嵌套层次。 这非常难看,但你还能指望什么呢? 如果引擎支持,则在匹配中使用反向引用。
不需要。您需要一个完整的parsing器来解决这类问题。