正则expression式匹配外括号
我需要一个正则expression式来select两个外括号之间的所有文本。
示例: some text(text here(possible text)text(possible text(more text)))end text
结果:( (text here(possible text)text(possible text(more text)))
我一直在努力几个小时,但是请注意,我的正则expression知识并不是我想要的:-)所以任何帮助都会被感激地收到。
正则expression式是工作的错误工具,因为你正在处理嵌套结构,即recursion。
但有一个简单的algorithm来做到这一点,我在这个答案中描述了一个前面的问题 。
你可以使用正则expression式recursion :
\(([^()]|(?R))*\)
我想添加这个答案快速参考。 随时更新。
.NET Regex使用平衡组 。
\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)
c
用作深度计数器。
演示Regexstorm.com
- 堆栈溢出 :使用RegEx来平衡匹配括号
- Wes的令人费解的博客 :与.NET正则expression式匹配的平衡构造
- Greg Reinacker的博客 :正则expression式中的嵌套构造
PCRE使用recursion模式 。
\((?>[^)(]+|(?R))*\)
演示在regex101 ; 或者没有改变:
\((?>[^)(]*(?R)?)*\)
演示在regex101 。 该图案被粘贴在代表(?0)
(?R)
(?0)
。
Perl,PHP,Notepad ++, R : perl = TRUE , Python :用于Perl行为的正则包 (?V1)
。
Ruby使用子expression式调用 。
用Ruby 2.0 \g<0>
可以用来调用全模式。
\((?>[^)(]+|\g<0>)*\)
在Rubular上演示 ; Ruby 1.9仅支持捕获组recursion :
(\((?>[^)(]+|\g<1>)*\))
Rubular演示 (自Ruby 1.9.3以来的primefaces分组 )
JavaScript API :: XRegExp.matchRecursive
XRegExp.matchRecursive(str, '\\(', '\\)', 'g');
JS,Java和其他正则expression式,无recursion最多2层嵌套:
\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)
演示在regex101 。 更深的嵌套需要添加到模式。
在不平衡圆括号上失败的速度会降低+
量词。
参考 – 这个正则expression式是什么意思?
- rexegg.com – recursion正则expression式
- Regular-Expressions.info – 正则expression式recursion
[^\(]*(\(.*\))[^\)]*
[^\(]*
匹配在string开头不是左括号的所有内容, (\(.*\))
捕获括号内所需的子string, [^\)]*
匹配所有不是'在string末尾的右括号。 请注意,此expression式不会尝试匹配括号; 一个简单的parsing器(见德曼的答案 )会更适合这个。
实际上可以使用.NET正则expression式来实现,但这并不是微不足道的,所以仔细阅读。
你可以在这里阅读一篇不错的文章。 您也可能需要阅读.NET正则expression式。 你可以从这里开始阅读。
使用尖括号<>
是因为它们不需要逸出。
正则expression式如下所示:
< [^<>]* ( ( (?<Open><) [^<>]* )+ ( (?<Close-Open>>) [^<>]* )+ )* (?(Open)(?!)) >
(?<=\().*(?=\))
如果你想在两个匹配的圆括号之间select文本,那么运用正则expression式是不可能的。 这是不可能的(*) 。
这个正则expression式只是返回string中第一个开始和最后一个结束括号之间的文本。
(*)除非你的正则expression式引擎具有平衡组或recursion等function 。 支持这些function的引擎数量正在慢慢增长,但它们仍然不是常用的。
使用Ruby(1.9.3或更高版本)的正则expression式:
/(?<match>\((?:\g<match>|[^()]++)*\))/
在rubular上演示
这是最终的正则expression式:
\( (?<arguments> ( ([^\(\)']*) | (\([^\(\)']*\)) | '(.*?)' )* ) \)
例:
input: ( arg1, arg2, arg3, (arg4), '(pip' ) output: arg1, arg2, arg3, (arg4), '(pip'
注意'(pip'
是正确的stringpipe理。(在调节器: http : //sourceforge.net/projects/regulator/ )
答案取决于你是否需要匹配匹配的括号集合,或者仅仅是input文本中的第一个打开到最后一个closures。
如果你需要匹配匹配的嵌套括号,那么你需要比正则expression式更多的东西。 – 见@德曼
如果这只是第一次打开最后closures,请参阅@Zach
决定你想要发生什么:
abc ( 123 ( foobar ) def ) xyz ) ghij
在这种情况下,您需要确定您的代码需要匹配。
我已经写了一个名为平衡的 JavaScript库来帮助完成这个任务,你可以通过这样做来完成这个任务
balanced.matches({ source: source, open: '(', close: ')' });
你甚至可以做replace
balanced.replacements({ source: source, open: '(', close: ')', replace: function (source, head, tail) { return head + source + tail; } });
inheritance人一个更复杂和交互式的例子JSFiddle
所以你需要第一个和最后一个括号,使用像这样str.indexOf('('); – 它会给你第一个发生str.lastIndexOf(')'); – 最后一个
所以你需要string之间,String searchingString = str.substring(str1.indexOf('('),str1.lastIndexOf(')');
这是一个可自定义的解决scheme,允许在Java中使用单个字符文字分隔符:
public static List<String> getBalancedSubstrings(String s, Character markStart, Character markEnd, Boolean includeMarkers) { List<String> subTreeList = new ArrayList<String>(); int level = 0; int lastOpenDelimiter = -1; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == markStart) { level++; if (level == 1) { lastOpenDelimiter = (includeMarkers ? i : i + 1); } } else if (c == markEnd) { if (level == 1) { subTreeList.add(s.substring(lastOpenDelimiter, (includeMarkers ? i + 1 : i))); } if (level > 0) level--; } } return subTreeList; } }
示例用法:
String s = "some text(text here(possible text)text(possible text(more text)))end text"; List<String> balanced = getBalancedSubstrings(s, '(', ')', true); System.out.println("Balanced substrings:\n" + balanced); // => [(text here(possible text)text(possible text(more text)))]
""" Here is a simple python program showing how to use regular expressions to write a paren-matching recursive parser. This parser recognises items enclosed by parens, brackets, braces and <> symbols, but is adaptable to any set of open/close patterns. This is where the re package greatly assists in parsing. """ import re # The pattern below recognises a sequence consisting of: # 1. Any characters not in the set of open/close strings. # 2. One of the open/close strings. # 3. The remainder of the string. # # There is no reason the opening pattern can't be the # same as the closing pattern, so quoted strings can # be included. However quotes are not ignored inside # quotes. More logic is needed for that.... pat = re.compile(""" ( .*? ) ( \( | \) | \[ | \] | \{ | \} | \< | \> | \' | \" | BEGIN | END | $ ) ( .* ) """, re.X) # The keys to the dictionary below are the opening strings, # and the values are the corresponding closing strings. # For example "(" is an opening string and ")" is its # closing string. matching = { "(" : ")", "[" : "]", "{" : "}", "<" : ">", '"' : '"', "'" : "'", "BEGIN" : "END" } # The procedure below matches string s and returns a # recursive list matching the nesting of the open/close # patterns in s. def matchnested(s, term=""): lst = [] while True: m = pat.match(s) if m.group(1) != "": lst.append(m.group(1)) if m.group(2) == term: return lst, m.group(3) if m.group(2) in matching: item, s = matchnested(m.group(3), matching[m.group(2)]) lst.append(m.group(2)) lst.append(item) lst.append(matching[m.group(2)]) else: raise ValueError("After <<%s %s>> expected %s not %s" % (lst, s, term, m.group(2))) # Unit test. if __name__ == "__main__": for s in ("simple string", """ "double quote" """, """ 'single quote' """, "one'two'three'four'five'six'seven", "one(two(three(four)five)six)seven", "one(two(three)four)five(six(seven)eight)nine", "one(two)three[four]five{six}seven<eight>nine", "one(two[three{four<five>six}seven]eight)nine", "oneBEGINtwo(threeBEGINfourENDfive)sixENDseven", "ERROR testing ((( mismatched ))] parens"): print "\ninput", s try: lst, s = matchnested(s) print "output", lst except ValueError as e: print str(e) print "done"
正则expression式不能做到这一点。
正则expression式基于称为Finite State Automata (FSA)
的计算模型。 正如名字所示, FSA
只能记住当前状态,没有关于以前状态的信息。
在上图中,S1和S2是两个状态,其中S1是开始和最后一个步骤。 所以如果我们尝试string0110
,转换过程如下:
0 1 1 0 -> S1 -> S2 -> S2 -> S2 ->S1
在上面的步骤中,当我们处于第二个S2
即0110
parsing01
之后,FSA没有关于01
前面的0
,因为它只能记住当前状态和下一个input符号。
在上面的问题中,我们需要知道左括号的否定; 这意味着它必须存储在某个地方。 但是由于FSAs
不能这样做,所以不能写正则expression式。
但是,可以写一个algorithm来实现这个目标。 algorithm通常属于Pushdown Automata (PDA)
。 PDA
是FSA
一个级别。 PDA有一个额外的堆栈来存储的东西。 PDA可以用来解决上述问题,因为我们可以在堆栈中“ push
”左括号,并在遇到右括号时“ pop
”它们。 如果最后,堆栈是空的,则打开括号和右括号匹配。 否则不是。
详细的讨论可以在这里find。