Próbuję znaleźć lepsze rozwiązanie do tworzenia parsera niektórych znanych formatów plików, takich jak: EDIFACT i TRADACOMS .
Jeśli nie znasz tych standardów, sprawdź ten przykład z Wikipedii:
Poniżej znajduje się przykład komunikatu EDIFACT wykorzystanego do odpowiedzi na żądanie dostępności produktu: -
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'
Segment UNA jest opcjonalny. Jeśli jest obecny, określa znaki specjalne, których należy użyć do interpretacji pozostałej części wiadomości. UNA ma sześć znaków w następującej kolejności:
- separator elementu danych komponentu (: w tym przykładzie)
- separator elementu danych (+ w tym przykładzie)
- powiadomienie dziesiętne (w tej próbce)
- znak zwolnienia (? w tym przykładzie)
- zastrzeżone, musi być spacją
- terminator segmentu („w tym przykładzie)
Jak widać, to tylko niektóre dane sformatowane w specjalny sposób, które czekają na parsowanie (podobnie jak pliki XML ).
Teraz mój system jest oparty na PHP i byłem w stanie stworzyć parser używając wyrażeń regularnych dla każdego segmentu, ale problem nie polega na tym, że wszyscy doskonale implementują standard.
Niektórzy dostawcy całkowicie ignorują opcjonalne segmenty i pola. Inni mogą zdecydować o wysłaniu większej ilości danych niż inni. Dlatego zmuszono mnie do utworzenia walidatorów dla segmentów i pól, aby sprawdzić, czy plik jest poprawny, czy nie.
Możesz sobie wyobrazić koszmar wyrażeń regularnych, który mam teraz. Ponadto każdy dostawca potrzebuje wielu modyfikacji wyrażeń regularnych, które zwykle buduję analizator składni dla każdego dostawcy.
Pytania:
1- Czy to najlepsza praktyka do analizowania plików (przy użyciu wyrażeń regularnych)?
2- Czy jest lepsze rozwiązanie do analizowania plików (być może istnieje gotowe rozwiązanie)? Czy będzie w stanie pokazać, którego segmentu brakuje lub czy plik jest uszkodzony?
3- Jeśli muszę mimo to zbudować analizator składni, jakiego wzoru lub metodologii powinienem użyć?
Uwagi:
Czytałem gdzieś o yacc i ANTLR, ale nie wiem, czy odpowiadają moim potrzebom, czy nie!
źródło
Odpowiedzi:
Potrzebujesz prawdziwego parsera. Wyrażenia regularne obsługują leksykację, a nie parsowanie. Oznacza to, że identyfikują tokeny w twoim strumieniu wejściowym. Analiza jest kontekstem tokenów, tzn. Kto idzie gdzie i w jakiej kolejności.
Klasycznym narzędziem analizującym jest yacc / bizon . Klasyczny lexer to lex / flex . Ponieważ php pozwala na integrację kodu C , możesz użyć flex i bison, aby zbudować parser, poprosić php o wywołanie go w pliku wejściowym / strumieniu, a następnie uzyskać wyniki.
Będzie on niesamowicie szybki i o wiele łatwiejszy w obsłudze, gdy zrozumiesz narzędzia . Proponuję przeczytać Lex i Yacc 2nd Ed. od O'Reilly. Na przykład utworzyłem projekt flex and bison na github z plikiem makefile. W razie potrzeby jest on kompatybilny z systemem Windows.
Jest to skomplikowane, ale jak dowiedział się, co trzeba zrobić, to skomplikowane. Istnieje wiele „rzeczy”, które należy wykonać dla poprawnie działającego parsera, a flex i bizon radzą sobie z bitami mechanicznymi. W przeciwnym razie znajdziesz się w niemożliwej do pozazdroszczenia pozycji pisania kodu na tej samej warstwie abstrakcji co asembler.
źródło
ouch .. parser „prawdziwy”? maszyny stanowe?
przepraszam, ale od czasu rozpoczęcia pracy przeszedłem z akademickiego na hakera. Powiedziałbym, że są łatwiejsze sposoby .. chociaż może nie tak „wyrafinowany” akademicko :)
Spróbuję zaproponować alternatywne podejście, z którym niektórzy mogą się zgadzać lub nie, ale MOŻE być bardzo praktyczne w środowisku pracy.
Ja bym;
stamtąd użyłbym klas dla typów danych. dzielenie separatorów komponentów i elementów oraz iteracja po zwróconych tablicach.
Dla mnie jest to ponowne użycie kodu, OO, niska kohezja i wysoce modułowy .. i łatwy do debugowania i programowania. prostsze jest lepsze.
do parsowania pliku nie potrzebujesz maszyn stanów lub czegokolwiek całkowicie skomplikowanego ... maszyny stanów dobrze nadają się do parsowania kodu, będziesz zaskoczony, jak potężny może być powyższy kod pseduo, gdy jest używany w kontekście OO.
ps. wcześniej pracowałem z bardzo podobnymi plikami :)
Więcej pseudo kodu opublikowano tutaj:
klasa
możesz użyć tego w ten sposób ...
i powiedz, że masz więcej niż jeden segment. użyj kolejki, aby je dodać, i zdobądź pierwszy, drugi itd. według potrzeb. Naprawdę reprezentujesz msg w obiekcie obj i podajesz obiektowym metodom wywoływania danych. możesz skorzystać z tego, tworząc również niestandardowe metody .. do dziedziczenia .. cóż, to inne pytanie i myślę, że możesz z łatwością je zastosować, jeśli rozumiesz
źródło
recognize X token and do Y
. Nie ma kontekstu, nie można mieć wielu stanów, przejście przez trywialną liczbę przypadków powoduje rozdęcie kodu, a obsługa błędów jest trudna. Uważam, że potrzebowałem tych funkcji w prawdziwym świecie w prawie wszystkich przypadkach. To pomija błędy w miarę wzrostu złożoności. Najtrudniejszą częścią jest ustawienie szkieletu i poznanie działania narzędzia. Przekrocz to i równie szybko coś wymieszaj.parseUNAsegemntForVendor1()
,parseUNAsegemntForVendor2()
,parseUNAsegemntForVendor3()
, ... itd), prawda?Czy próbowałeś google dla „PHP EDIFACT”? To jeden z pierwszych wyników, który się pojawił: http://code.google.com/p/edieasy/
Chociaż może nie być wystarczający dla twojego przypadku użycia, możesz być w stanie uzyskać z niego kilka pomysłów. Nie podoba mi się kod z wieloma zagnieżdżonymi pętlami i warunkami, ale może to być początek.
źródło
Odkąd wspomniano o Yacc / Bison + Flex / Lex, równie dobrze mogę rzucić jedną z innych głównych alternatyw: kombinatory parsera. Są one popularne w programowaniu funkcjonalnym, takim jak Haskell, ale jeśli możesz połączyć się z kodem C, możesz ich użyć i, co wiesz, ktoś napisał również dla PHP. (Nie mam doświadczenia z tą konkretną implementacją, ale jeśli działa jak większość z nich, powinna być całkiem niezła).
Ogólna koncepcja jest taka, że zaczynasz od zestawu małych, łatwych do zdefiniowania parserów, zwykle tokenizerów. Tak jakbyś miał jedną funkcję analizatora składni dla każdego z 6 wspomnianych elementów danych. Następnie używasz kombinatorów (funkcji łączących funkcje) do tworzenia większych parserów, które chwytają większe elementy. Podobnie jak opcjonalny segment byłby
optional
kombinator działający na analizatorze segmentów.Nie jestem pewien, jak dobrze działa w PHP, ale to świetny sposób na napisanie parsera i bardzo lubię je używać w innych językach.
źródło
zamiast majstrować przy wyrażeniach regularnych stwórz własną maszynę stanową
będzie to bardziej czytelne (i będzie mogło mieć lepsze komentarze) w sytuacjach nietrywialnych i łatwiej będzie debugować tę czarną skrzynkę, która jest wyrażeniem regularnym
źródło
Nie wiem, co chcesz później zrobić z tymi danymi i czy nie jest to młot do orzechów, ale miałem dobre doświadczenia z Eli . Opisujesz zwroty leksykalne, a następnie konkretną / abstrakcyjną składnię i generujesz to, co chcesz wygenerować.
źródło