我需要一个正则表达式来选择两个外括号之间的所有文本。
例子: START_TEXT(这里的文本(可能的文本)文本(可能的文本(更多的文本))END_TXT ^ ^
结果: (此处文本(可能的文本)文本(可能的文本(更多的文本)))
我需要一个正则表达式来选择两个外括号之间的所有文本。
例子: START_TEXT(这里的文本(可能的文本)文本(可能的文本(更多的文本))END_TXT ^ ^
结果: (此处文本(可能的文本)文本(可能的文本(更多的文本)))
当前回答
这个答案解释了为什么正则表达式不是这项任务的正确工具的理论局限性。
正则表达式不能做到这一点。
正则表达式基于有限状态自动机(FSA)的计算模型。顾名思义,FSA只能记住当前状态,它没有关于以前状态的信息。
在上图中,S1和S2是两种状态,其中S1是开始和结束步骤。因此,如果我们尝试使用字符串0110,转换如下:
0 1 1 0
-> S1 -> S2 -> S2 -> S2 ->S1
在上述步骤中,当我们在第二个S2,即解析完0110的01之后,FSA没有关于01中前一个0的信息,因为它只能记住当前状态和下一个输入符号。
在上面的问题中,我们需要知道左括号的no;这意味着它必须存储在某个地方。但是由于fsa不能这样做,因此不能编写正则表达式。
但是,可以编写一个算法来完成这项任务。算法一般属于下推自动机(PDA)。PDA比FSA高一级。PDA有一个额外的堆栈来存储一些额外的信息。pda可以用来解决上述问题,因为我们可以在堆栈中“推入”开括号,并在遇到闭括号时“弹出”它们。如果在结束时,堆栈为空,则开始括号和结束括号匹配。否则不。
其他回答
正则表达式是一个错误的工具,因为你正在处理嵌套结构,即递归。
但是有一个简单的算法可以做到这一点,我在之前的问题的回答中详细描述了它。其要点是编写代码扫描字符串,并对尚未与闭括号匹配的开括号保持计数器。当计数器返回0时,您就知道已经到达了最后的右括号。
你可以使用regex递归:
\(([^()]|(?R))*\)
这可能有助于匹配平衡括号。
\s*\w+[(][^+]*[)]\s*
(?<=\().*(?=\))
如果您想在两个匹配的括号之间选择文本,那么使用正则表达式就不太走运了。这是不可能的。
这个正则表达式只返回字符串中第一个开始括号和最后一个结束括号之间的文本。
(*)除非你的regex引擎有像平衡组或递归这样的特性。支持这些特性的引擎的数量正在缓慢增长,但它们仍然不是普遍可用的。
"""
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"