如何在Python中将结构化字符串解析为Tree数据结构?
解析自定义嵌套笔记格式为树结构
我之前也碰到过类似的自定义嵌套格式解析问题,这种非标准的键值嵌套结构很容易在递归/迭代处理时踩坑——要么是层级跟踪混乱,要么是没处理好分号、大括号的边界。给你分享一个可行的实现方案,分步骤来解决:
1. 先定义树节点结构
首先我们需要一个简单的树节点类,用来存储每个层级的名称和它的子节点:
class TreeNode: def __init__(self, name): self.name = name self.children = [] def __repr__(self, level=0): # 自定义打印格式,方便直观查看树结构 ret = "\t" * level + f"|- {self.name}\n" for child in self.children: ret += child.__repr__(level + 1) return ret
2. 迭代式解析嵌套字符串
用栈来跟踪当前的层级(比递归更适合处理深层嵌套的场景,避免栈溢出),逐个字符处理输入字符串,区分键、子节点和叶子描述:
def parse_note_to_tree(note_str): # 预处理:去除多余空格、末尾点号,减少解析干扰 cleaned = note_str.replace(" ", "").replace(".", "") stack = [] root = None current_key = "" i = 0 n = len(cleaned) while i < n: char = cleaned[i] if char == ":": # 提取当前键(从上次记录的起始位置到当前冒号) current_key = cleaned[stack[-1][1] if stack else 0:i].strip() i += 1 # 冒号后是大括号,说明是子节点层级 if cleaned[i] == "{": node = TreeNode(current_key) if not root: root = node else: stack[-1][0].children.append(node) # 将当前节点和下一个键的起始位置压入栈 stack.append((node, i + 1)) i += 1 # 冒号后是普通文本,说明是叶子节点(描述内容) else: # 找到分号或大括号作为描述的结束标记 end_idx = cleaned.find(";", i) if end_idx == -1: end_idx = cleaned.find("}", i) desc = cleaned[i:end_idx].strip() leaf_node = TreeNode(desc) stack[-1][0].children.append(leaf_node) i = end_idx + 1 elif char == "}": # 闭合大括号,回到父节点层级 stack.pop() i += 1 # 跳过闭合后的分号(同级项分隔符) if i < n and cleaned[i] == ";": i += 1 elif char == ";": # 分号分隔同级项,更新下一个键的起始位置 if stack: stack[-1] = (stack[-1][0], i + 1) i += 1 else: i += 1 return root
3. 测试示例
用你给出的笔记格式测试一下:
sample_note = """CourseName: { Part 1: { I.I - Intro: { Topic1: { descr1; descr2: { 2.a; 2.b; 2.c. }; descr3. }; Topic2: { descr: { example. }. }. }; I.II - NextChapter: { Topic3: { whatever. }. }. }; Part 2: { II.I - FinalChapter: { content. }. }. }""" tree = parse_note_to_tree(sample_note) print(tree)
运行后会输出清晰的树结构:
|- CourseName |- Part1 |- I.I-Intro |- Topic1 |- descr1 |- descr2 |- 2a |- 2b |- 2c |- descr3 |- Topic2 |- descr |- example |- I.II-NextChapter |- Topic3 |- whatever |- Part2 |- II.I-FinalChapter |- content
你之前失败可能的原因
- 没有预处理字符串,多余的空格、点号干扰了键和描述的提取;
- 递归处理时没正确跟踪层级,导致子节点挂载错误;
- 没区分“冒号后接大括号(子节点)”和“冒号后接文本(叶子描述)”两种情况。
内容的提问来源于stack exchange,提问作者Flynn Rhodes
相关产品推荐
相关产品推荐

