如何获得平衡括号之间的表达式
Posted
技术标签:
【中文标题】如何获得平衡括号之间的表达式【英文标题】:How to get an expression between balanced parentheses 【发布时间】:2016-11-07 18:47:30 【问题描述】:假设我得到以下类型的字符串:
"(this is (haha) a string(()and it's sneaky)) ipsom (lorem) bla"
我想提取包含在括号的最顶层中的子字符串。 IE。我要获取字符串:"this is (haha) a string(()and it's sneaky)"
和"lorem"
。
有没有很好的pythonic方法来做到这一点?正则表达式显然可以胜任这项任务,但也许有办法让 xml 解析器完成这项工作?对于我的应用程序,我可以假设括号格式正确,即不是像 (()(() 这样的东西。
【问题讨论】:
我认为你应该为此定义一个函数。在该函数中,遍历字符串并维护一个标志以检查您是否在括号的最顶层。使用此方法,您可以获得开始和结束的索引,然后您可以提取字符串并连接到最终答案 这会被视为“pythonic”方法吗?我会通过使用 ctr 来解决它,它会在点击“(”时增加,在点击“)”时减少。当它在达到至少 1 个 '(' 后达到 0 时,您可以将初始位置和最终位置之间的子字符串附加到列表中。 嗨 utkarsh13。感谢那。这或多或少是我想到的解决方案,但我想知道是否有更快的内置在 python 中的一些功能,可以在几行易于阅读的行中完成。 @user177955 又快又脏:print re.match(string.replace(")",").").replace("(",".("), string).groups()[0::4]
。抱歉,我无法抗拒:这个字符串看起来太像一个正则表达式,我把它变成了一个正则表达式。 :P 话虽如此,您应该真正编写自己的堆栈或遵循 utkarsh 所说的内容。
@SuperSaiyan 当然,对于任何字符串,都存在一个任意复杂的 RE 可以完成这项工作:-P
【参考方案1】:
这是堆栈的标准用例:您按字符读取字符串,并且每当遇到左括号时,将符号压入堆栈;如果遇到右括号,则将符号从堆栈中弹出。
由于您只有一种类型的括号,因此您实际上不需要堆栈;相反,只需记住有多少个左括号就足够了。
此外,为了提取文本,我们还记得第一级括号打开时部分的开始位置,并在遇到匹配的右括号时收集结果字符串。
这可能看起来像这样:
string = "(this is (haha) a string(()and it's sneaky)) ipsom (lorem) bla"
stack = 0
startIndex = None
results = []
for i, c in enumerate(string):
if c == '(':
if stack == 0:
startIndex = i + 1 # string to extract starts one index later
# push to stack
stack += 1
elif c == ')':
# pop stack
stack -= 1
if stack == 0:
results.append(string[startIndex:i])
print(results)
# ["this is (haha) a string(()and it's sneaky)", 'lorem']
【讨论】:
@poke。感谢您编写 utkarsh13 和 Vaibhav Bajaj 的 cmets。我有一个小问题for i,c in enumerate(string)
是如何工作的?
@user177955 迭代enumerate(x)
将在每次迭代中为您提供一个二元组,除了可迭代的值之外,还有索引。因此,我们不是从字符串中获取每个字符,而是将字符与其在字符串中的索引配对。【参考方案2】:
这不是很“pythonic”......但是
def find_strings_inside(what_open,what_close,s):
stack = []
msg = []
for c in s:
s1=""
if c == what_open:
stack.append(c)
if len(stack) == 1:
continue
elif c == what_close and stack:
stack.pop()
if not stack:
yield "".join(msg)
msg[:] = []
if stack:
msg.append(c)
x= list(find_strings_inside("(",")","(this is (haha) a string(()and it's sneaky)) ipsom (lorem) bla"))
print x
【讨论】:
【参考方案3】:你确定正则表达式不够好?
>>> x=re.compile(r'\((?:(?:\(.*?\))|(?:[^\(\)]*?))\)')
>>> x.findall("(this is (haha) a string(()and it's sneaky)) ipsom (lorem) bla")
["(this is (haha) a string(()and it's sneaky)", '(lorem)']
>>> x.findall("((((this is (haha) a string((a(s)d)and ((it's sneaky))))))) ipsom (lorem) bla")
["((((this is (haha) a string((a(s)d)and ((it's sneaky))", '(lorem)']
【讨论】:
我没有投反对票。但是正则表达式并不是用于需要堆栈的地方的工具。我也应该为在 cmets 中提出相同的建议而感到羞耻(但这只是为了好玩;)) afaik 有一些内置的正则表达式包(我认为字面意思是import regexp
)已经扩展了对需要堆栈的东西的支持....afaik ...我仍然不赞成这个解决方案的正则表达式恕我直言)
@JoranBeasley 这不是“你应该盲目地使用它,因为它是正则表达式而且它很好”,更多地证明了“正则表达式显然不能胜任这项任务”的说法完全错误,因为他们可以做到。
我可以给你一个字符串,我很确定我可以打破这个正则表达式......向前看的东西让人很难猜到(我当然没有投反对票,如果正则表达式有效,那就太好了:P)
考虑 "((((this is (haha) a string((a(s)d)and ((it's sneaky))))))) ipsom (lorem) bla"
... 除非你 100% 确定最大嵌套深度 ... 即使这样,正则表达式也会变得很丑【参考方案4】:
这或多或少重复了已经说过的内容,但可能更容易阅读:
def extract(string):
flag = 0
result, accum = [], []
for c in string:
if c == ')':
flag -= 1
if flag:
accum.append(c)
if c == '(':
flag += 1
if not flag and accum:
result.append(''.join(accum))
accum = []
return result
>> print extract(test)
["this is (haha) a string(()and it's sneaky)", 'lorem']
【讨论】:
以上是关于如何获得平衡括号之间的表达式的主要内容,如果未能解决你的问题,请参考以下文章