通过Python移除所有嵌套块,保留非嵌套块
来源:
[This] is some text with [some [blocks that are nested [in a [variety] of ways]]]
结果文本:
[This] is some text with
我觉得用正则表达式来处理这个问题可能不太行,从在 Stack Overflow 的讨论来看。
有没有简单的方法可以做到这一点?还是说必须使用 pyparsing(或者其他解析库)呢?
4 个回答
5
这里有一个简单的方法,不需要任何额外的工具:扫描文本,并记录你经过的括号数量。每当你看到一个“[”时,就把计数器加一;每当你看到一个“]”时,就把计数器减一。
- 只要计数器的值是零或一,就把你看到的文本放到输出字符串里。
- 如果计数器的值大于一,那说明你在一个嵌套的块里,所以就不要把这个文本放到输出字符串里。
- 如果最后计数器的值不是零,说明字符串有问题;你开了和关了括号的数量不一样。(如果大于零,说明多了那么多个
[
;如果小于零,说明多了那么多个]
。)
4
以提问者的例子为标准(任何包含进一步嵌套块的部分都必须被移除),那么...:
import itertools
x = '''[This] is some text with [some [blocks that are nested [in a [variety]
of ways]]] and some [which are not], and [any [with nesting] must go] away.'''
def nonest(txt):
pieces = []
d = 0
level = []
for c in txt:
if c == '[': d += 1
level.append(d)
if c == ']': d -= 1
for k, g in itertools.groupby(zip(txt, level), lambda x: x[1]>0):
block = list(g)
if max(d for c, d in block) > 1: continue
pieces.append(''.join(c for c, d in block))
print ''.join(pieces)
nonest(x)
这段代码会输出
[This] is some text with and some [which are not], and away.
根据这个标准假设,似乎是我们想要的结果。
这个想法是,在level
中计算一个并行的计数列表,表示“我们在这个点上嵌套了多少层”(也就是说,到目前为止我们遇到了多少个打开但还没关闭的括号);然后用groupby
将level
和文本结合,分成交替的块,一个是没有嵌套的,另一个是有嵌套的。对于每个块,我们会计算其中的最大嵌套层数(对于没有嵌套的块,这个值会保持为零——更一般来说,就是整个块中的最大嵌套层数),如果得到的嵌套层数小于等于1,那么对应的文本块就会被保留。需要注意的是,我们需要将组g
转换成一个列表block
,因为我们想进行两次迭代(一次是获取最大嵌套层数,一次是将字符重新组合成文本块)——如果要在一次迭代中完成,就需要在嵌套循环中保持一些辅助状态,这在这种情况下会稍微麻烦一些。