通过Python移除所有嵌套块,保留非嵌套块

3 投票
4 回答
1134 浏览
提问于 2025-04-15 17:23

来源:

[This] is some text with [some [blocks that are nested [in a [variety] of ways]]]

结果文本:

[This] is some text with

我觉得用正则表达式来处理这个问题可能不太行,从在 Stack Overflow 的讨论来看。

有没有简单的方法可以做到这一点?还是说必须使用 pyparsing(或者其他解析库)呢?

4 个回答

3

如果你写一个解析器会更好,特别是如果你使用像 pyparsing 这样的解析器生成工具。这样做会更容易维护和扩展。

实际上,pyparsing 已经为你实现了一个 解析器,你只需要写一个函数来过滤解析器的输出结果就可以了。

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中计算一个并行的计数列表,表示“我们在这个点上嵌套了多少层”(也就是说,到目前为止我们遇到了多少个打开但还没关闭的括号);然后用groupbylevel和文本结合,分成交替的块,一个是没有嵌套的,另一个是有嵌套的。对于每个块,我们会计算其中的最大嵌套层数(对于没有嵌套的块,这个值会保持为零——更一般来说,就是整个块中的最大嵌套层数),如果得到的嵌套层数小于等于1,那么对应的文本块就会被保留。需要注意的是,我们需要将组g转换成一个列表block,因为我们想进行两次迭代(一次是获取最大嵌套层数,一次是将字符重新组合成文本块)——如果要在一次迭代中完成,就需要在嵌套循环中保持一些辅助状态,这在这种情况下会稍微麻烦一些。

撰写回答