是否可以编写与未出现次数的嵌套模式匹配的正则表达式?例如,当外括号内嵌有未知数量的打开/关闭括号时,正则表达式是否可以匹配开括号和右括号?
例如:
public MyMethod()
{
if (test)
{
// More { }
}
// More { }
} // End
应匹配:
{
if (test)
{
// More { }
}
// More { }
}
答案 0 :(得分:254)
没有。就这么简单。有限自动机(正则表达式下面的数据结构)除了它所处的状态之外没有内存,如果你有任意深度的嵌套,你需要一个任意大的自动机,它与的概念相冲突有限自动机。
您可以将嵌套/配对元素匹配到固定深度,其深度仅受内存限制,因为自动机变得非常大。然而,在实践中,您应该使用下推自动机,即用于无上下文语法的解析器,例如LL(自上而下)或LR(自下而上)。您必须考虑更糟糕的运行时行为:O(n ^ 3)与O(n),n =长度(输入)。
有许多可解析的解析器生成器,例如Java的ANTLR。找到Java(或C)的现有语法也并不困难 更多背景信息:维基百科的Automata Theory
答案 1 :(得分:36)
使用正则表达式检查嵌套模式非常简单。
'/(\((?>[^()]+|(?1))*\))/'
答案 2 :(得分:33)
如果字符串在一行上,可能正在使用Perl解决方案:
my $NesteD ;
$NesteD = qr/ \{( [^{}] | (??{ $NesteD }) )* \} /x ;
if ( $Stringy =~ m/\b( \w+$NesteD )/x ) {
print "Found: $1\n" ;
}
HTH
编辑:检查:
还有一件事Torsten Marek(谁正确地指出,它不再是一个正则表达式):
答案 3 :(得分:19)
是的,如果它是.NET RegEx引擎。 .Net引擎支持随外部堆栈提供的有限状态机。见details
答案 4 :(得分:15)
Pumping lemma for regular languages是您不能这样做的原因。
生成的自动机将具有有限数量的状态,比如k,因此一串k + 1个开口括号必然会在某处重复一个状态(因为自动机处理字符)。同一状态之间的字符串部分可以无限次复制,自动机不会知道差异。
特别是,如果它接受k + 1个开口支撑,接着是k + 1个闭合支撑(它应该),它也会接受抽出的开口支撑数量,然后是未改变的k + 1个闭合支撑(它不应该)。
答案 5 :(得分:13)
正确的正则表达式将无法执行此操作,因为您将使常规语言领域在Context Free Languages区域中着陆。
然而,许多语言提供的“正则表达式”包非常强大。
例如,Lua正则表达式具有匹配平衡括号的“%b()
”识别器。在您的情况下,您将使用“%b{}
”
另一个类似于sed的复杂工具是gema,您可以使用{#}
非常轻松地匹配平衡花括号。
因此,根据您可以使用的工具,您的“正则表达式”(在更广泛的意义上)可能能够匹配嵌套的括号。
答案 6 :(得分:5)
使用PHP正则表达式引擎中的递归匹配比括号的程序匹配快得多。特别是长弦。
http://php.net/manual/en/regexp.reference.recursive.php
e.g。
$patt = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!x';
preg_match_all( $patt, $str, $m );
VS
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;
}
答案 7 :(得分:4)
...假设有一些最大数量的嵌套你很乐意停下来。
让我解释一下。
@torsten-marek是正确的,正则表达式无法检查这样的嵌套模式,但是可以定义一个嵌套的正则表达式模式,它将允许你捕获像这样的嵌套结构,直到最大深度。我创建了一个用于捕获EBNF-style条评论(try it out here),例如:
(* This is a comment (* this is nested inside (* another level! *) hey *) yo *)
正则表达式(针对单深度注释)如下:
m{1} = \(+\*+(?:[^*(]|(?:\*+[^)*])|(?:\(+[^*(]))*\*+\)+
通过将\(+\*+
和\*+\)+
替换为{
和}
并使用简单的[^{}]
替换其中的所有内容,可以轻松地将其调整为适合您的用途:
p{1} = \{(?:[^{}])*\}
(Here's the link试试这个。)
要嵌套,只需在块本身内使用此模式:
p{2} = \{(?:(?:p{1})|(?:[^{}]))*\}
...or...
p{2} = \{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\}
要查找三重嵌套块,请使用:
p{3} = \{(?:(?:p{2})|(?:[^{}]))*\}
...or...
p{3} = \{(?:(?:\{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\})|(?:[^{}]))*\}
出现了明显的模式。要查找嵌套到N
深度的注释,只需使用正则表达式:
p{N} = \{(?:(?:p{N-1})|(?:[^{}]))*\}
where N > 1 and
p{1} = \{(?:[^{}])*\}
可以编写脚本来递归生成这些正则表达式,但这超出了我需要的范围。 (这是留给读者的练习。)
答案 8 :(得分:3)
/(?>[^{}]*){(?>[^{}]*)(?R)*(?>[^{}]*)}/sm
答案 9 :(得分:2)
不,那时你正在进入Context Free Grammars领域。
答案 10 :(得分:0)
这似乎有效:/(\{(?:\{.*\}|[^\{])*\})/m