从嵌套括号内的 python 获取字符串

Getting string from python within nested parentheses

我有一个像

这样的字符串

(0 (1 (2 (3 (4 (5 The) (6 room)) (7 (8 was) (9 (10 very) (11 good)))) (12 but)) (13 (14 (15 the) (16 food)) (17 (18 was) (19 (20 very) (21 bad))))) (22 .))

实际上是一棵树:

我想获得给定节点的字符串,即如果说节点 0 我应该收到 "The room was very good but food was bad." 如果我说节点 2 我应该收到 "The room was very good but" 并且对于节点 5 "The"等等。

我会首先构建明显的树(其中节点有子节点和可能的字符串有效负载)然后处理它以获得您想要的替代方案(带有包含子节点有效负载的字符串)。例如,草稿(无错误检查 &c):

class Node(object):
    def __init__(self, n):
        self.n = n
        self.children = []
        self.text = []
        self.payload = self.wholestring = ''

def make_payload_tree(astring):
    root = Node(-1)
    parents = [root]
    sit = iter(astring)
    for c in sit:
        if c=='(':
            mkn = []
            for c in sit:
                if c==' ': break
                mkn.append(c)
            newnode = Node(int(''.join(mkn)))
            parents[-1].children.append(newnode)
            parents.append(newnode)
        elif c==')':
            oldnode = parents.pop()
            oldnode.payload = ''.join(oldnode.text)
        else:
            parents[-1].text.append(c)
  return root

您可以粗略地验证此有效负载树是否正确,例如:

def print_tree(r, ind=0):
    print ' '*ind, r.n, r.payload, r.wholestring
    for c in r.children:
        print_tree(c, ind + 2)

当然,此时wholestring仍然是空字符串。

现在,第二遍可让您构建 wholestring 属性:

def makewhole(node):
    for c in node.children:
        makewhole(c)
    s = node.payload + ' '.join(c.wholestring for c in node.children)
    node.wholestring = s.replace('  ', ' ')

print_tree 应验证您是否拥有所需的 wholestring

现在 有趣的 部分是进行正确的错误诊断(如果输入字符串中有任何 "syntax error",则此代码非常脆弱,并且语法您的示例暗示了这一点,从未明确表示过),但这可能最好使用适当的词法分析器和解析器方法来完成,而不是像我在这里做的那样使用临时解析。

您可以使用 pyparsing:

解析字符串
s='(0 (1 (2 (3 (4 (5 The) (6 room)) (7 (8 was) (9 (10 very) (11 good)))) (12 but)) (13 (14 (15 the) (16 food)) (17 (18 was) (19 (20 very) (21 bad))))) (22 .))'

from pyparsing import *
enclosed = Forward()
nestedParens = nestedExpr('(', ')', content=enclosed) 
enclosed << (Word(alphanums+'.') | ',' | nestedParens)

>>> enclosed.parseString(s).asList()  
[['0', ['1', ['2', ['3', ['4', ['5', 'The'], ['6', 'room']], ['7', ['8', 'was'], ['9', ['10', 'very'], ['11', 'good']]]], ['12', 'but']], ['13', ['14', ['15', 'the'], ['16', 'food']], ['17', ['18', 'was'], ['19', ['20', 'very'], ['21', 'bad']]]]], ['22', '.']]]

然后对嵌套数据进行适当的处​​理。

第一个问题,如何解析字符串。

每个 node 看起来都像 ({number} {word | node node}),其中 number\d+word[\w,.]+。此描述非常适合递归下降解析器。

第二个问题是,如何存储生成的树,以便我可以轻松找到节点及其后代。

我建议 dict 按节点编号索引 - 类似于

tree = {
    0: (1, 22),
    1: (2, 13),
    2: (3, 12),
    # ...
    20: "very",
    21: "bad",
    22: "."
}

这允许您直接跳转到任何节点,并从那里沿着树重新创建句子片段。