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',]]

私はさまざまなウィキ マークアップ言語、マークダウン、再構成されたテキストなどを見てきましたが、大量のタグと構文をカバーする必要があるため、どのように機能するかを理解するのは非常に複雑です (「リスト」のみが必要です)。これらのほとんどの部分ですが、もちろん html ではなく python に変換されています。)

トークナイザー、レクサー、パーサーも調べましたが、これらは必要以上に複雑であり、理解できます。

どこから始めればよいかわかりません。この件に関して可能な限りの助けをいただければ幸いです。ありがとう

Edit-1 : はい、行頭の文字が重要です。以前の必要な出力から、*は子を持つルート ノードを示し、+には子があり、-には子がありません (ルートまたはその他) ) であり、そのノードに関する単なる追加情報です。は重要ではなく、 +*と交換できます(他の方法で 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ための中間ステップとして明示的なクラスを使用しています-ロジックは、行のリストをノードのリストに変換し、次にそのリストを変換することですノードをツリーに(インデント属性を適切に使用して)作成し、そのツリーを読み取り可能な形式で出力します(これは、ツリーが適切に構築されていることを確認するための単なる「デバッグヘルプ」ステップであり、もちろんコメントアウトできます。スクリプトの最終バージョン-もちろん、デバッグ用にハードコードするのではなく、ファイルから行を取得します!-)、最後に目的のPython構造を構築して印刷します。これがコードです。後で説明するように、結果はOPが指定したものとほぼ同じですが、1つの例外がありますが、最初のコードは次のとおりです。

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で保証されていない)を除けば、これはほとんど要求どおりです-ここですべてのメモが、キーとnotes文字列のリストである値を持つdictエントリとして表示されることを除いて(ただし、リストが空になる場合は、おおよそ質問の例のように、メモのエントリは省略されます)。

質問の現在のバージョンでは、メモをどのように表現するかが少し不明確です。1つのメモはスタンドアロンの文字列として表示され、他のメモは値が文字列であるエントリとして表示されます(私が使用している文字列のリストではありません)。メモが1つのケースではスタンドアロンの文字列として、他のすべてのケースではdictエントリとして表示される必要があることを意味するものが明確ではないため、私が使用しているこのスキームはより規則的です。また、メモ(ある場合)がリストではなく単一の文字列である場合、ノードに複数のメモが表示されるとエラーになることを意味しますか?後者に関しては、私が使用しているこのスキームはより一般的です(ノードに、質問で明らかに示されている0または1だけでなく、0から上までの任意の数のノートを持たせます)。

目的のソリューションの99%を提供するために(事前編集の回答はほぼ同じくらい長く、仕様の明確化と変更に役立ちました)非常に多くのコードを記述したので、これが元のポスターを満足させることを願っています。それらを互いに一致させるためのコードおよび/または仕様は、彼にとって簡単なはずです!

于 2009-07-07T05:16:12.480 に答える
1

アウトラインの状況を扱っているので、スタックを使用して物事を単純化できます。基本的にdictは、アウトラインの深さに対応する s を持つスタックを作成する必要があります。新しい行を解析し、アウトラインの深さが増したら、スタックの一番上に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 に答える