2009-07-07 5 views
5

Siehe aktualisierte Eingabe- und Ausgabedaten bei Edit-1.Wie kann ich markierten Text für die weitere Verarbeitung analysieren?

Was ich versuche

 
+ 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 

in eine Python-Datenstruktur dreht zu erreichen wie

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

ich an vielen verschiedenen Wiki Markup-Sprachen haben gesucht, Abschlags, umstrukturiert Text, etc, aber sie sind alle extrem kompliziert für mich zu verstehen, wie es funktioniert, da sie eine große Menge an Tags und Syntax abdecken müssen (ich würde nur die "Liste" Teile der meisten von diesen benötigen, aber natürlich zu Python anstelle von HTML konvertiert.)

Ich habe mir auch Tokenizer, Lexer und Parser angeschaut, aber wieder sind sie viel komplizierter als ich brauche und die ich verstehen kann.

Ich habe keine Ahnung, wo ich anfangen soll und würde jede mögliche Hilfe zu diesem Thema schätzen. Dank

Bearbeiten-1: Ja, die Zeichen am Anfang der Zeile Angelegenheiten, von der geforderten Leistung aus der Zeit vor und jetzt ist es zu sehen sind, dass die * einen Wurzelknoten mit Kindern bezeichnet, die + hat Kinder und die - hat keine Kinder (root oder sonst) und ist nur zusätzliche Informationen zu diesem Knoten. Die * ist nicht wichtig und kann mit + (I root bekommen können andere Wege Status.) Ausgetauscht werden

Deshalb ist die neue Anforderung nur * verwendet würde, um einen Knoten mit oder ohne Kinder zu bezeichnen und - kann keine Kinder haben. Ich habe es auch geändert, also ist der Schlüssel nicht der Text nach dem *, denn das wird ohne Zweifel später zu einem tatsächlichen Titel wechseln.

Zum Beispiel

 
* 1 
* 1.1 
* 1.2 
    - Note for 1.2 
* 2 
* 3 
- Note for root 

würde

[{'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'] 

Oder wenn Sie eine andere Idee haben die Umrisse in Python darzustellen, die sie dann nach vorne bringen.

+0

Geschehen und getan. Ich habe beide diese Dinge bearbeitet. – Rigsby

Antwort

6

bearbeiten: Dank der Klärung und Änderung der spec ich meinen Code bearbeitet haben, noch eine explizite Node Klasse als Zwischenschritt für Klarheit mit - die Logik ist die Liste der Linien in eine Liste zu machen von Knoten, dann wandle diese Liste von Knoten in einen Baum um (indem ihr Einzug-Attribut entsprechend verwendet wird), drucke dann diesen Baum in einer lesbaren Form aus (dies ist nur ein "Debug-Hilfe" -Schritt, um zu überprüfen, ob der Baum gut konstruiert ist) kann natürlich in der finalen Version des Skripts auskommentiert werden - die, wie natürlich, die Zeilen aus einer Datei nehmen wird, anstatt sie zum Debuggen fest codieren zu lassen! -), schließlich die gewünschte Python-Struktur erstellen und ausdrucken. Hier ist der Code, und als wir nach sehen, dass das Ergebnis fast wie die OP mit einer Ausnahme gibt - aber zuerst den Code:

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() 

Bei der Ausführung dieses aussendet:

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

Neben der Bestellung von Schlüsseln (die natürlich in einem dict immateriell und kann nicht garantiert ist), dieser ist fast wie gewünscht - außer dass hier alle Noten als dict Einträge mit einem Schlüssel von notes erscheinen und ein Wert, der a ist Liste der Strings (aber der Eintrag notes entfällt, wenn die Liste leer wäre, ungefähr so ​​wie im Beispiel in der Frage).

In der aktuellen Version der Frage, wie die Notizen darzustellen ist leicht unklar; Eine Notiz wird als eigenständige Zeichenfolge angezeigt, andere als Einträge, deren Wert eine Zeichenfolge ist (anstelle einer Liste von Zeichenfolgen, die ich verwende). Es ist nicht klar, was bedeuten soll, dass die Notiz in einem Fall als eigenständige Zeichenfolge und in allen anderen als dict-Eintrag angezeigt werden muss. Daher ist dieses Schema, das ich verwende, regelmäßiger; und wenn eine Notiz (falls vorhanden) eine einzelne Zeichenfolge statt einer Liste ist, würde das bedeuten, dass es ein Fehler ist, wenn mehr als eine Notiz für einen Knoten erscheint? In letzterer Hinsicht ist dieses Schema, das ich benutze, allgemeiner (lässt einen Knoten eine beliebige Anzahl von Noten von 0 aufwärts haben, anstatt nur 0 oder 1, wie es in der Frage impliziert ist).

Nachdem so viel Code geschrieben wurde (die Pre-Edit-Antwort war etwa so lang und geholfen, die Spezifikationen zu klären und zu ändern), um (hoffentlich) 99% der gewünschten Lösung zu liefern, hoffe ich, dass dies das ursprüngliche Poster befriedigt Die letzten paar Änderungen an Code und/oder Spezifikationen, damit sie zueinander passen, sollten für ihn einfach sein!

+0

Ich habe meinen Beitrag aktualisiert, um Dinge zu klären.Jetzt zeige ich, dass die * oder - Materie und ich die erste Ausgabe repariert haben (die {'1.2.3'} hätte nur eine Zeichenfolge sein sollen und kein Diktat wie ich). – Rigsby

1

Da es sich um eine Umrisssituation handelt, können Sie die Dinge vereinfachen, indem Sie einen Stapel verwenden. Grundsätzlich möchten Sie einen Stapel erstellen, der dict s entspricht, der der Tiefe des Umrisses entspricht. Wenn Sie eine neue Linie analysieren und die Tiefe des Umrisses erhöht wurde, schieben Sie einen neuen dict auf den Stapel, auf den der vorherige dict oben im Stapel verwiesen hat. Wenn Sie eine Linie analysieren, die eine geringere Tiefe aufweist, blenden Sie den Stapel ein, um zum übergeordneten Objekt zurückzukehren. Und wenn Sie auf eine Linie treffen, die die gleiche Tiefe hat, fügen Sie sie zu der dict oben im Stapel hinzu.

+0

Und um wirklich schick zu werden, können Sie den Inhalt der Elemente und re.match verwenden, um sicherzustellen, dass der nächste Eintrag mit einem Pluspunkt plus Zahlen beginnt. – Kurt

1

Stapel sind eine sehr nützliche Datenstruktur beim Parsen von Bäumen. Sie behalten den Pfad vom zuletzt hinzugefügten Knoten bis zum Stamm im Stapel immer bei, so dass Sie nach der Länge des Einzuges das richtige übergeordnete Element finden können. So etwas sollte für das Parsen von Ihrem letzten Beispiel arbeiten:

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] 
0

Die Syntax, Sie sind mit ist sehr ähnlich wie Yaml. Es hat einige Unterschiede, aber es ist ziemlich einfach zu lernen - es ist der Schwerpunkt liegt auf Menschen lesbar (und beschreibbar).

Werfen Sie einen Blick auf Yaml Website. Es gibt einige Python-Bindungen, Dokumentation und andere Sachen dort.