正则表达式匹配外括号

Dav*_*veF 266 regex

我需要一个正则表达式来选择两个外括号之间的所有文本.

例: some text(text here(possible text)text(possible text(more text)))end text

结果: (text here(possible text)text(possible text(more text)))

Fra*_*ank 131

正则表达式是工作的错误工具,因为您正在处理嵌套结构,即递归.

但是有一个简单的算法可以做到这一点,我在前一个问题的答案中对此进行了描述.

  • 我不同意正则表达式是错误的工具,原因有几个.1)大多数正则表达式实现都有一个可行但如果不是完美的解决方案.2)通常,你试图在一个上下文中找到平衡的分隔符对,其中也适用于正则表达式的其他标准.3)通常你将正则表达式交给一些只接受正则表达式的API,你别无选择. (22认同)
  • 正则表达式是这项工作的正确工具.这个答案是对的.请参阅rogal111的回答. (19认同)
  • .NET的实现有[平衡组定义http://msdn.microsoft.com/en-us/library/bs2twtah.aspx#balancing_group_definition]允许这种事情. (14认同)
  • 绝对同意答案.虽然在regexp中有一些递归的实现,但它们等于有限状态机,并且不支持使用嵌套结构,但Context Free Grammars会这样做.看看霍姆斯基的正式语法的层次结构. (4认同)
  • 弗兰克是对的,上下文无关语法不能用正则表达式来描述。这就是这个答案的关键点。 (4认同)
  • 语言纯粹主义者正确地认为乔姆斯基“常规”语言明确排除了递归。这并不意味着正则表达式必须是常规的。当然,这个标签有点错误,但是该语言的语法足够好,可以让人们完全解决用例,否则 90% 是由现代正则表达式的真正常规子集解决的。 (3认同)

rog*_*111 110

你可以使用正则表达式递归:

\(([^()]|(?R))*\)
Run Code Online (Sandbox Code Playgroud)

  • 在.NET 4.5中,我得到以下错误:"无法识别的分组构造". (5认同)
  • @AndyHayden这是因为“(1,(2,3))(4,5)”有两个用空格隔开的组。将我的正则表达式与全局标志一起使用:/ \(([[((^()] ||(?R))* \)/ g。这是在线测试:http://regex101.com/r/lF0fI1/1 (4认同)
  • 一个例子在这里真的很有用,我不能让它适用于像"(1,(2,3))(4,5)"这样的东西. (3认同)
  • 真棒!这是正则表达式的一个很好的特性.感谢您是唯一真正回答这个问题的人.此外,regex101网站很甜蜜. (3认同)
  • 附带说明一下,这有点命名错误,因为[真正的正则表达式不是递归的](https://en.wikipedia.org/wiki/Chomsky_hierarchy)。 (2认同)

bob*_*ble 107

我想添加这个答案以便快速参考.随意更新.


.NET Regex使用平衡组.

\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)
Run Code Online (Sandbox Code Playgroud)

在哪里c用作深度计数器.

在Regexstorm.com上演示


PCRE使用递归模式.

\((?:[^)(]+|(?R))*+\)
Run Code Online (Sandbox Code Playgroud)

在regex101演示 ; 或者没有替换:

\((?:[^)(]*(?R)?)*+\)
Run Code Online (Sandbox Code Playgroud)

在regex101演示 ; 或展开性能:

\([^)(]*+(?:(?R)[^)(]*)*+\)
Run Code Online (Sandbox Code Playgroud)

在regex101演示 ; 粘贴的图案(?R)代表(?0).

的Perl,PHP,记事本++, - [R :perl的= TRUE,Python的:正则表达式包(?V1)为Perl行为.


Ruby使用子表达式调用.

使用Ruby 2.0 \g<0>可以用来调用完整模式.

\((?>[^)(]+|\g<0>)*\)
Run Code Online (Sandbox Code Playgroud)

在Rubular演示 ; Ruby 1.9仅支持捕获组递归:

(\((?>[^)(]+|\g<1>)*\))
Run Code Online (Sandbox Code Playgroud)

Rubular演示  (Ruby 1.9.3以来的原子分组)


JavaScript  API :: XRegExp.matchRecursive

XRegExp.matchRecursive(str, '\\(', '\\)', 'g');
Run Code Online (Sandbox Code Playgroud)

JS,Java和其他正则表达式,没有递归,最多可达2级嵌套:

\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)
Run Code Online (Sandbox Code Playgroud)

在regex101演示.需要将更深的嵌套添加到模式中.
要在不平衡的括号上更快地失败,请丢弃+量词.


Java:使用@jaytea的前向引用有趣的想法.


参考 - 这个正则表达式意味着什么?

  • 对于递归,我建议使用 `(\((?:[^)(]+|(?R))*+\)` 而不是 `\((?:[^)(]+|(?R))*+\)` )*+\))` (或 `?2`、`?3` 等,具体取决于它是什么数字组)。`?R` 总是递归回到表达式的开头。如果你单独使用这个,那就没问题了。但例如,如果您在 `if` 语句后面发现逻辑比较,则 `if \((?:[^)(]+|(?R))*+\)` 将不匹配任何内容,因为 `if ` 也必须重复才能匹配,而不仅仅是括号。然而, `if (\((?:[^)(]+|(?1))*+\))` 只会检查 `if` 一次,然后递归检查第一组。 (3认同)
  • 最佳答案,谢谢您的研究! (2认同)

Zac*_*ena 28

[^\(]*(\(.*\))[^\)]*
Run Code Online (Sandbox Code Playgroud)

[^\(]*匹配字符串开头不是(\(.*\))左括号的[^\)]*所有内容,捕获括在括号中的所需子字符串,并匹配字符串末尾不是右括号的所有内容.请注意,此表达式不会尝试匹配括号; 一个简单的解析器(参见dehmann的答案)会更适合它.

  • 这个expr不能像"text(text)text(text)text"返回"(text)text(text)"这样的东西.正则表达式不能计算括号. (10认同)

Tom*_*lak 17

(?<=\().*(?=\))
Run Code Online (Sandbox Code Playgroud)

如果要在两个匹配的括号中选择文本,则表示您对正则表达式不满意.这是不可能的(*).

此正则表达式只返回字符串中第一个开头和最后一个右括号之间的文本.


(*)除非您的正则表达式引擎具有平衡组递归等功能.支持此类功能的引擎数量正在缓慢增长,但它们仍然不常用.

  • @ghayes答案是从2009年开始的.那是*很久以前的事了; 允许某种形式的递归的正则表达式引擎比现在更常见(并且它们*仍然非常罕见).我会在答案中提到它. (3认同)
  • 这是环视,或更准确地说是“零宽度前瞻/后视断言”。大多数现代正则表达式引擎都支持它们。 (2认同)

Ale*_*osh 12

实际上可以使用.NET正则表达式来完成它,但它并不是微不足道的,所以请仔细阅读.

你可以在这里阅读一篇好文章.您还可能需要阅读.NET正则表达式.你可以在这里开始阅读.

<>使用尖括号是因为它们不需要转义.

正则表达式如下所示:

<
[^<>]*
(
    (
        (?<Open><)
        [^<>]*
    )+
    (
        (?<Close-Open>>)
        [^<>]*
    )+
)*
(?(Open)(?!))
>
Run Code Online (Sandbox Code Playgroud)


mus*_*ibs 12

这个答案解释了为什么正则表达式不是执行此任务的正确工具的理论限制。


正则表达式不能做到这一点。

正则表达式基于称为的计算模型Finite State Automata (FSA)。顾名思义,a FSA只能记住当前状态,而没有关于先前状态的信息。

金融服务管理局

在上图中,S1和S2是两个状态,其中S1是开始步骤和最终步骤。因此,如果我们尝试使用字符串0110,则转换如下:

      0     1     1     0
-> S1 -> S2 -> S2 -> S2 ->S1
Run Code Online (Sandbox Code Playgroud)

在上述步骤中,当我们处于第二个位置时,S2即在解析01之后0110,FSA没有关于前一个输入的信息001因为它只能记住当前状态和下一个输入符号。

在上述问题中,我们需要知道开括号的编号;这意味着它必须存储在某个地方。但是由于FSAs不能做到这一点,所以不能编写正则表达式。

但是,可以编写算法来完成此任务。算法通常属于Pushdown Automata (PDA)PDA是的上一级FSA。PDA有一个额外的堆栈来存储一些其他信息。PDA可用于解决上述问题,因为我们可以push在堆栈中“打开括号pop”,并在遇到闭合括号时“”。如果最后堆栈是空的,则打开括号和关闭括号匹配。否则不行。

  • @Marco 这个答案从理论角度讨论了正则表达式。现在许多正则表达式引擎不仅依赖于这个理论模型,而且使用一些额外的内存来完成这项工作! (3认同)
  • @JiříHerník:严格来说,这些不是正则表达式:*Kleene* 没有定义为正则表达式。一些正则表达式引擎确实实现了一些额外的功能,使它们不仅仅是解析*常规语言*。 (3认同)
  • 这里有几个答案,这证明,这是可能的。 (2认同)

Mar*_*rco 6

这是最终的正则表达式:

\(
(?<arguments> 
(  
  ([^\(\)']*) |  
  (\([^\(\)']*\)) |
  '(.*?)'

)*
)
\)
Run Code Online (Sandbox Code Playgroud)

例子:

input: ( arg1, arg2, arg3, (arg4), '(pip' )

output: arg1, arg2, arg3, (arg4), '(pip'
Run Code Online (Sandbox Code Playgroud)

请注意,'(pip'是作为字符串正确管理的。(在监管机构中尝试:http : //sourceforge.net/projects/regulator/


Man*_*ish 6

我也陷入了嵌套模式出现的这种情况。

正则表达式是解决上述问题的正确方法。使用下面的模式

'/(\((?>[^()]+|(?1))*\))/'
Run Code Online (Sandbox Code Playgroud)

  • 作为在类似主题上寻求帮助的用户,我不知道该正则表达式具体做什么,以及如何使用它来将其应用于我自己的问题。也许这是一个很好的答案,但考虑到正则表达式的神秘性,我必须查找它的每一部分,看看这是否对我有帮助。鉴于这种“解决方案”有很多答案,我认为我不会。 (2认同)

小智 5

使用Ruby的正则表达式(1.9.3或以上版本):

/(?<match>\((?:\g<match>|[^()]++)*\))/
Run Code Online (Sandbox Code Playgroud)

rubular 上的演示


Cha*_*ira 5

我编写了一个名为Balanced 的小型 JavaScript 库来帮助完成此任务。你可以通过这样做来完成这个任务

balanced.matches({
    source: source,
    open: '(',
    close: ')'
});
Run Code Online (Sandbox Code Playgroud)

您甚至可以进行替换:

balanced.replacements({
    source: source,
    open: '(',
    close: ')',
    replace: function (source, head, tail) {
        return head + source + tail;
    }
});
Run Code Online (Sandbox Code Playgroud)

这是一个更复杂且交互式的示例JSFiddle


Wik*_*żew 5

除了bobble bubble 的答案之外,还有其他支持递归构造的正则表达式风格。

\n

卢阿

\n

使用%b()%b{}/%b[]代表大括号/方括号):

\n
    \n
  • for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end(参见演示
  • \n
\n

Raku(以前的 Perl6)

\n

非重叠的多个平衡括号匹配:

\n
my regex paren_any { \'(\' ~ \')\' [ <-[()]>+ || <&paren_any> ]* }\nsay "Extract (a(b)c) and ((d)f(g))" ~~ m:g/<&paren_any>/;\n# => (\xef\xbd\xa2(a(b)c)\xef\xbd\xa3 \xef\xbd\xa2((d)f(g))\xef\xbd\xa3)\n
Run Code Online (Sandbox Code Playgroud)\n

重叠多个平衡括号匹配:

\n
say "Extract (a(b)c) and ((d)f(g))" ~~ m:ov:g/<&paren_any>/;\n# => (\xef\xbd\xa2(a(b)c)\xef\xbd\xa3 \xef\xbd\xa2(b)\xef\xbd\xa3 \xef\xbd\xa2((d)f(g))\xef\xbd\xa3 \xef\xbd\xa2(d)\xef\xbd\xa3 \xef\xbd\xa2(g)\xef\xbd\xa3)\n
Run Code Online (Sandbox Code Playgroud)\n

请参阅演示

\n

Pythonre非正则表达式解决方案

\n

请参阅poke's 答案How to get an expression Between Balanced Parentheses

\n

Java可定制的非正则表达式解决方案

\n

这是一个可定制的解决方案,允许在 Java 中使用单字符文字分隔符:

\n
public static List<String> getBalancedSubstrings(String s, Character markStart, \n                                 Character markEnd, Boolean includeMarkers) \n\n{\n        List<String> subTreeList = new ArrayList<String>();\n        int level = 0;\n        int lastOpenDelimiter = -1;\n        for (int i = 0; i < s.length(); i++) {\n            char c = s.charAt(i);\n            if (c == markStart) {\n                level++;\n                if (level == 1) {\n                    lastOpenDelimiter = (includeMarkers ? i : i + 1);\n                }\n            }\n            else if (c == markEnd) {\n                if (level == 1) {\n                    subTreeList.add(s.substring(lastOpenDelimiter, (includeMarkers ? i + 1 : i)));\n                }\n                if (level > 0) level--;\n            }\n        }\n        return subTreeList;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

使用示例:

\n
String s = "some text(text here(possible text)text(possible text(more text)))end text";\nList<String> balanced = getBalancedSubstrings(s, \'(\', \')\', true);\nSystem.out.println("Balanced substrings:\\n" + balanced);\n// => [(text here(possible text)text(possible text(more text)))]\n
Run Code Online (Sandbox Code Playgroud)\n


归档时间:

查看次数:

317506 次

最近记录:

6 年 前