5

在 Edit-1 中查看更新的输入和输出数据。

我想要完成的是转

+ 1
 + 1.1
  + 1.1.1
   - 1.1.1.1
   - 1.1.1.2
 + 1.2
  - 1.2.1
  - 1.2.2
 - 1.3
+ 2
- 3

进入python数据结构,例如

[{'1': [{'1.1': {'1.1.1': ['1.1.1.1', '1.1.1.2']}, '1.2': ['1.2.1', '1.2.2']}, '1.3'], '2': {}}, ['3',]]

我查看了许多不同的 wiki 标记语言、markdown、重组文本等,但它们对于我来说理解它是如何工作的都非常复杂,因为它们必须涵盖大量的标签和语法(我只需要“列表”其中大部分的一部分,但当然转换为python而不是html。)

我还研究了分词器、词法分析器和解析器,但它们比我需要的要复杂得多,而且我能理解。

我不知道从哪里开始,并希望在这个主题上提供任何帮助。谢谢

Edit-1:是的,行首的字符很重要,从之前的所需输出现在可以看出,*表示有子节点的根节点,+有子节点, -没有子节点(root 或其他) 并且只是与该节点有关的额外信息。*不重要,可以与 + 互换(可以通过其他方式获得 root 状态。)

因此,新要求将仅*用于表示有或没有子节点的节点,并且-不能有子节点。我也改变了它,所以关键不是后面的文本,*因为这无疑会在以后变成一个实际的标题。

例如

* 1
 * 1.1
 * 1.2
  - 1.2 的注意事项
* 2
* 3
- 根注意

会给

[{'title': '1', 'children': [{'title': '1.1', 'children': []}, {'title': '1.2', 'children': []}]}, {'title': '2', 'children': [], 'notes': ['Note for 1.2', ]}, {'title': '3', 'children': []}, 'Note for root']

或者,如果您有另一个想法来表示 Python 中的轮廓,那么请提出来。

4

4 回答 4

6

编辑:由于规范中的澄清和更改,我编辑了我的代码,仍然使用显式Node类作为中间步骤以清晰起见——逻辑是将行列表转换为节点列表,然后转换该列表将节点放入树中(通过适当地使用它们的 indent 属性),然后以可读的形式打印该树(这只是一个“调试帮助”步骤,以检查树是否构造良好,当然可以在脚本的最终版本——当然,它会从文件中获取行而不是硬编码以进行调试!-),最后构建所需的 Python 结构并打印它。这是代码,正如我们之后将看到的,结果几乎与 OP 指定的一样,但有一个例外——但是,首先是代码:

import sys

class Node(object):
  def __init__(self, title, indent):
    self.title = title
    self.indent = indent
    self.children = []
    self.notes = []
    self.parent = None
  def __repr__(self):
    return 'Node(%s, %s, %r, %s)' % (
        self.indent, self.parent, self.title, self.notes)
  def aspython(self):
    result = dict(title=self.title, children=topython(self.children))
    if self.notes:
      result['notes'] = self.notes
    return result

def print_tree(node):
  print ' ' * node.indent, node.title
  for subnode in node.children:
    print_tree(subnode)
  for note in node.notes:
    print ' ' * node.indent, 'Note:', note

def topython(nodelist):
  return [node.aspython() for node in nodelist]

def lines_to_tree(lines):
  nodes = []
  for line in lines:
    indent = len(line) - len(line.lstrip())
    marker, body = line.strip().split(None, 1)
    if marker == '*':
      nodes.append(Node(body, indent))
    elif marker == '-':
      nodes[-1].notes.append(body)
    else:
      print>>sys.stderr, "Invalid marker %r" % marker

  tree = Node('', -1)
  curr = tree
  for node in nodes:
    while node.indent <= curr.indent:
      curr = curr.parent
    node.parent = curr
    curr.children.append(node)
    curr = node

  return tree


data = """\
* 1
 * 1.1
 * 1.2
  - Note for 1.2
* 2
* 3
- Note for root
""".splitlines()

def main():
  tree = lines_to_tree(data)
  print_tree(tree)
  print
  alist = topython(tree.children)
  print alist

if __name__ == '__main__':
  main()

运行时,它会发出:

 1
  1.1
  1.2
  Note: 1.2
 2
 3
 Note: 3

[{'children': [{'children': [], 'title': '1.1'}, {'notes': ['Note for 1.2'], 'children': [], 'title': '1.2'}], 'title': '1'}, {'children': [], 'title': '2'}, {'notes': ['Note for root'], 'children': [], 'title': '3'}]

除了键的顺序(这在 dict 中是无关紧要的,当然也不能保证),这几乎符合要求——除了这里所有的注释都显示为 dict 条目,键为notes,值是字符串列表(但如果列表为空,则注释条目将被省略,大致如问题示例中所做的那样)。

在当前版本的问题中,如何表示笔记有点不清楚;一个音符显示为独立字符串,其他音符显示为值为字符串的条目(而不是我使用的字符串列表)。目前尚不清楚在一种情况下注释必须作为独立字符串出现,在所有其他情况下作为字典条目出现是什么意思,所以我使用的这个方案更常规;如果一个注释(如果有的话)是一个字符串而不是一个列表,这是否意味着如果一个节点出现多个注释是错误的?在后一方面,我使用的这个方案更通用(让一个节点从 0 开始有任意数量的音符,而不是问题中明显暗示的只有 0 或 1)。

编写了这么多代码(预编辑答案大约一样长,并有助于澄清和更改规范)以提供(我希望)99% 的所需解决方案,我希望这能满足原始海报,因为最后几次调整使它们相互匹配的代码和/或规范对他来说应该很容易做到!

于 2009-07-07T05:16:12.480 回答
1

由于您正在处理大纲情况,因此您可以使用堆栈来简化事情。基本上,您想创建一个dicts 对应于轮廓深度的堆栈。当您解析一个新行并且轮廓的深度增加时,您将一个新行推送到堆栈顶部的dict前一个引用的堆栈上。dict当您解析具有较低深度的行时,您会弹出堆栈以返回父级。当你遇到一条具有相同深度的线时,你将它添加到dict堆栈的顶部。

于 2009-07-07T04:59:28.397 回答
1

在解析树时,堆栈是一种非常有用的数据结构。您只需始终保留从最后添加的节点到堆栈根的路径,以便您可以通过缩进的长度找到正确的父节点。像这样的东西应该适用于解析你的最后一个例子:

import re
line_tokens = re.compile('( *)(\\*|-) (.*)')

def parse_tree(data):
    stack = [{'title': 'Root node', 'children': []}]
    for line in data.split("\n"):
        indent, symbol, content = line_tokens.match(line).groups()        
        while len(indent) + 1 < len(stack):
            stack.pop() # Remove everything up to current parent
        if symbol == '-':
            stack[-1].setdefault('notes', []).append(content)
        elif symbol == '*':
            node = {'title': content, 'children': []}
            stack[-1]['children'].append(node)
            stack.append(node) # Add as the current deepest node
    return stack[0]
于 2009-07-07T09:47:20.187 回答
0

您使用的语法与 Yaml非常相似。它有一些区别,但很容易学习——它的主要重点是人类可读(和可写)。

看看 Yaml 网站。那里有一些 python 绑定、文档和其他东西。

于 2009-07-21T13:45:08.390 回答