1

这个正则表达式

/\(.*\)/

不会匹配匹配的括号,而是字符串中的最后一个括号。是否有正则表达式扩展或类似的东西,具有允许这样做的适当语法?例如:

there are (many (things (on) the)) box (except (carrots (and apples)))

/OPEN(.*CLOSE)/应该匹配(many (things (on) the))

括号可能有无限的层次。

4

3 回答 3

7

如果你只有一层括号,那么有两种可能性。

选项 1:使用不贪婪的重复:

/\(.*?\)/

当它遇到第一个).

选项 2:使用否定字符类

/\([^)]*\)/

这只能重复不是 的字符),因此它必然永远不会超过第一个右括号。由于性能原因,通常首选此选项。此外,这个选项更容易扩展以允许转义括号(这样你就可以匹配这个完整的字符串:(some\)thing)而不是扔掉thing))。但这可能很少需要。

但是,如果你想要嵌套结构,这对于正则表达式来说通常太复杂了(尽管 PCRE 等一些风格支持递归模式)。在这种情况下,您应该自己遍历字符串并计算括号,以跟踪您当前的嵌套级别。

就像关于这些递归模式的旁注一样:在 PCRE(?R)中只表示整个模式,因此将其插入某处会使整个事物递归。但是括号的每个内容必须与整个匹配具有相同的结构。此外,实际上不可能用它来进行有意义的一步替换,以及在多个嵌套级别上使用捕获组。总而言之 - 你最好不要对嵌套结构使用正则表达式。

更新:由于您似乎渴望找到一个正则表达式解决方案,以下是您如何使用 PCRE 匹配您的示例(PHP 中的示例实现):

$str = 'there are (many (things (on) the)) box (except (carrots (and apples)))';
preg_match_all('/\([^()]*(?:(?R)[^()]*)*\)/', $str, $matches);
print_r($matches);

结果是

Array
(
    [0] => Array
        (
            [0] => (many (things (on) the))
            [1] => (except (carrots (and apples)))
        )   
)

模式的作用:

\(      # opening bracket
[^()]*  # arbitrarily many non-bracket characters
(?:     # start a non-capturing group for later repetition
(?R)    # recursion! (match any nested brackets)
[^()]*  # arbitrarily many non-bracket characters
)*      # close the group and repeat it arbitrarily many times
\)      # closing bracket

这允许无限嵌套级别以及无限并行级别。

请注意,不可能将所有嵌套级别作为单独的捕获组。您将始终只获得最内层或最外层的组。此外,像这样进行递归替换是不可能的。

于 2012-10-28T20:13:28.683 回答
2

正则表达式不足以找到匹配的括号,因为括号是嵌套结构。但是,有一个简单的算法可以找到匹配的括号,这在这个答案中有描述。

如果您只是想在表达式中找到第一个右括号,则应在正则表达式中使用非贪婪匹配器。在这种情况下,您的正则表达式的非贪婪版本如下:

/\(.*?\)/
于 2012-10-28T20:14:29.947 回答
1

给定一个包含嵌套匹配括号的字符串,您可以使用这个(非递归 JavaScript)正则表达式匹配最里面的集合:

var re = /\([^()]*\)/g;

或者你可以用这个(递归 PHP)正则表达式匹配最外层的集合:

$re = '/\((?:[^()]++|(?R))*\)/';

但是您不能轻松地匹配位于最内层和最外层之间的匹配括号集。

还要注意(天真和经常遇到的)表达式:/\(.*?\)/将始终不正确地匹配(既不是最内层也不是最外层匹配集)。

于 2012-10-28T20:47:47.527 回答