Ich versuche, eine bessere Lösung zu finden, um einen Parser für einige der bekannten Dateiformate wie EDIFACT und TRADACOMS zu erstellen .
Wenn Sie mit diesen Standards nicht vertraut sind, lesen Sie dieses Beispiel aus Wikipedia:
Im Folgenden finden Sie ein Beispiel für eine EDIFACT-Nachricht, die zur Beantwortung einer Produktverfügbarkeitsanfrage verwendet wird: -
UNA:+.? '
UNB+IATB:1+6XPPC+LHPPC+940101:0950+1'
UNH+1+PAORES:93:1:IA'
MSG+1:45'
IFT+3+XYZCOMPANY AVAILABILITY'
ERC+A7V:1:AMD'
IFT+3+NO MORE FLIGHTS'
ODI'
TVL+240493:1000::1220+FRA+JFK+DL+400+C'
PDI++C:3+Y::3+F::1'
APD+714C:0:::6++++++6X'
TVL+240493:1740::2030+JFK+MIA+DL+081+C'
PDI++C:4'
APD+EM2:0:130::6+++++++DA'
UNT+13+1'
UNZ+1+1'
Das UNA-Segment ist optional. Wenn vorhanden, gibt es die Sonderzeichen an, die zur Interpretation des Restes der Nachricht verwendet werden sollen. Es gibt sechs Zeichen nach UNA in dieser Reihenfolge:
- Komponentendatenelementtrennzeichen (: in diesem Beispiel)
- Datenelementtrennzeichen (+ in diesem Beispiel)
- Dezimalbenachrichtigung (. in diesem Beispiel)
- Release-Charakter (? in diesem Beispiel)
- reserviert, muss ein Platz sein
- Segmentterminator ('in diesem Beispiel)
Wie Sie sehen, sind es nur einige Daten, die auf spezielle Weise formatiert wurden und darauf warten, analysiert zu werden (ähnlich wie XML- Dateien).
Jetzt basiert mein System auf PHP und ich konnte einen Parser mit regulären Ausdrücken für jedes Segment erstellen, aber das Problem ist, dass nicht jeder den Standard perfekt implementiert.
Einige Lieferanten neigen dazu, optionale Segmente und Felder vollständig zu ignorieren. Andere senden möglicherweise mehr Daten als andere. Aus diesem Grund musste ich Validatoren für Segmente und Felder erstellen, um zu testen, ob die Datei korrekt war oder nicht.
Sie können sich den Albtraum der regulären Ausdrücke vorstellen, die ich gerade habe. Darüber hinaus benötigt jeder Lieferant viele Änderungen an den regulären Ausdrücken, sodass ich für jeden Lieferanten einen Parser erstelle.
Fragen:
1- Ist dies die beste Vorgehensweise zum Parsen von Dateien (unter Verwendung regulärer Ausdrücke)?
2- Gibt es eine bessere Lösung für das Parsen von Dateien (vielleicht gibt es da draußen eine fertige Lösung)? Kann es zeigen, welches Segment fehlt oder ob die Datei beschädigt ist?
3- Wenn ich meinen Parser trotzdem erstellen muss, welches Entwurfsmuster oder welche Methodik sollte ich verwenden?
Anmerkungen:
Ich habe irgendwo über Yacc und ANTLR gelesen, aber ich weiß nicht, ob sie meinen Bedürfnissen entsprechen oder nicht!
quelle
Antworten:
Was Sie brauchen, ist ein echter Parser. Reguläre Ausdrücke behandeln Lexing, nicht Parsing. Das heißt, sie identifizieren Token in Ihrem Eingabestream. Das Parsen ist der Kontext der Token, dh wer wohin und in welcher Reihenfolge geht.
Das klassische Parsing-Tool ist Yacc / Bison . Der klassische Lexer ist Lex / Flex . Da PHP die Integration von C-Code ermöglicht , können Sie Ihren Parser mit Flex und Bison erstellen, PHP in der Eingabedatei / im Stream aufrufen lassen und dann Ihre Ergebnisse abrufen.
Es wird blitzschnell und viel einfacher zu bearbeiten sein, sobald Sie die Werkzeuge verstanden haben . Ich schlage vor, Lex und Yacc 2nd Ed zu lesen . von O'Reilly. Zum Beispiel habe ich ein Flex- und Bison-Projekt auf Github mit einem Makefile eingerichtet. Es ist bei Bedarf für Windows crosskompilierbar.
Es ist komplex, aber wie Sie herausgefunden haben, ist das, was Sie tun müssen, komplex. Es gibt eine Menge "Dinge", die für einen ordnungsgemäß funktionierenden Parser erledigt werden müssen, und Flex und Bison kümmern sich um die mechanischen Teile. Andernfalls befinden Sie sich in der nicht beneidenswerten Position, Code auf derselben Abstraktionsschicht wie Assembly zu schreiben.
quelle
autsch .. "wahrer" Parser? Zustandsautomaten?
Entschuldigung, aber ich bin seit Beginn meiner Anstellung vom Akademiker zum Hacker konvertiert worden. Ich würde also sagen, dass es einfachere Wege gibt, obwohl ich akademisch vielleicht nicht so „verfeinert“ bin :)
Ich werde versuchen, einen alternativen Ansatz anzubieten, dem einige zustimmen oder nicht zustimmen, der jedoch in einer Arbeitsumgebung sehr praktisch sein kann.
Ich würde;
von dort würde ich Klassen für die Datentypen verwenden. Aufteilen von Komponenten- und Elementtrennzeichen und Durchlaufen der zurückgegebenen Arrays.
Für mich ist dies Code-Wiederverwendung, OO, geringe Kohäsion und hochmodular .. und einfach zu debuggen und zu programmieren. einfacher ist besser.
Um eine Datei zu analysieren, benötigen Sie keine Zustandsautomaten oder etwas völlig Kompliziertes. Zustandsmaschinen eignen sich gut zum Analysieren von Code. Sie werden überrascht sein, wie leistungsfähig der obige Pseduo-Code sein kann, wenn er in einem OO-Kontext verwendet wird.
ps. Ich habe schon mit sehr ähnlichen Dateien gearbeitet :)
Weitere Pseudocodes finden Sie hier:
Klasse
Sie könnten es dann so verwenden ..
und sagen Sie, Sie haben mehr als ein Segment. Verwenden Sie eine Warteschlange, um sie hinzuzufügen und das erste, zweite usw. nach Bedarf abzurufen. Sie stellen die Nachricht wirklich nur in einem Objekt dar und geben die Objektmethoden zum Aufrufen der Daten an. Sie könnten dies ausnutzen, indem Sie auch benutzerdefinierte Methoden für die Vererbung erstellen. Nun, das ist eine andere Frage, und ich denke, Sie könnten sie leicht anwenden, wenn Sie sie verstehen
quelle
recognize X token and do Y
. Es gibt keinen Kontext, Sie können nicht mehrere Zustände haben, wenn Sie eine triviale Anzahl von Fällen überwinden, wird der Code aufgebläht, und die Fehlerbehandlung ist schwierig. Ich finde, dass ich diese Funktionen in fast allen Fällen in der realen Welt benötigt habe. Das lässt Fehler beiseite, wenn die Komplexität zunimmt. Am schwierigsten ist es, ein Skelett aufzubauen und zu lernen, wie das Werkzeug funktioniert. Überwinde das und es ist genauso schnell, etwas zu zaubern.parseUNAsegemntForVendor1()
,parseUNAsegemntForVendor2()
,parseUNAsegemntForVendor3()
, ... etc), nicht wahr?Haben Sie versucht, nach "PHP EDIFACT" zu googeln? Dies ist eines der ersten Ergebnisse, die aufgetaucht sind: http://code.google.com/p/edieasy/
Während es für Ihren Anwendungsfall möglicherweise nicht ausreicht, können Sie möglicherweise einige Ideen daraus gewinnen. Ich mag den Code mit seinen vielen verschachtelten for-Schleifen und Bedingungen nicht, aber es kann ein Anfang sein.
quelle
Nun, da Yacc / Bison + Flex / Lex erwähnt wurden, könnte ich genauso gut eine der anderen Hauptalternativen einwerfen: Parser-Kombinatoren. Diese sind in der funktionalen Programmierung wie bei Haskell beliebt, aber wenn Sie eine Schnittstelle zu C-Code herstellen können, können Sie sie verwenden, und was wissen Sie, jemand hat auch eine für PHP geschrieben. (Ich habe keine Erfahrung mit dieser speziellen Implementierung, aber wenn es wie die meisten von ihnen funktioniert, sollte es ziemlich nett sein.)
Das allgemeine Konzept besteht darin, dass Sie mit einer Reihe kleiner, einfach zu definierender Parser beginnen, normalerweise Tokenizer. Als hätten Sie eine Parser-Funktion für jedes der 6 Datenelemente, die Sie erwähnt haben. Dann verwenden Sie Kombinatoren (Funktionen, die Funktionen kombinieren), um größere Parser zu erstellen, die größere Elemente erfassen. Wie ein optionales Segment wäre der
optional
Kombinator, der auf dem Segmentparser arbeitet.Ich bin mir nicht sicher, wie gut es in PHP funktioniert, aber es macht Spaß, einen Parser zu schreiben, und ich genieße es sehr, sie in anderen Sprachen zu verwenden.
quelle
Anstatt mit Regexen zu spielen, erstellen Sie Ihre eigene Zustandsmaschine
Dies ist in nicht trivialen Situationen besser lesbar (und kann bessere Kommentare abgeben) und es ist einfacher, die Blackbox zu debuggen, bei der es sich um Regex handelt
quelle
Ich weiß nicht, was Sie danach genau mit diesen Daten machen wollen und ob es kein Vorschlaghammer für eine Nuss ist, aber ich hatte gute Erfahrungen mit eli . Sie beschreiben die lexikalischen Phrasen und dann die konkrete / abstrakte Syntax und generieren, was Sie generieren möchten.
quelle