Il modo migliore per analizzare un file

9

Sto cercando di trovare una soluzione migliore per fare un parser ad alcuni dei famosi formati di file come: EDIFACT e TRADACOMS .

Se non hai familiarità con questi standard, dai un'occhiata a questo esempio di Wikipedia:

See below for an example of an EDIFACT message used to answer to a product availability request:-

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'

Il segmento UNA è opzionale. Se presente, specifica i caratteri speciali che devono essere utilizzati per interpretare il resto del messaggio. Ci sono sei caratteri che seguono UNA in questo ordine:

  • separatore di elementi di dati componente (: in questo esempio)
  • separatore di elementi di dati (+ in questo esempio)
  • notifica decimale (. in questo esempio)
  • rilascia carattere (? in questo esempio)
  • riservato, deve essere uno spazio
  • terminatore di segmento ('in questo esempio)

Come puoi vedere, sono solo alcuni dati formattati in un modo speciale in attesa di essere analizzati (in modo simile ai file XML ).

Ora il mio sistema è basato su PHP e sono riuscito a creare parser usando espressioni regolari per ogni segmento, ma il problema non è che tutti implementino perfettamente lo standard.

Alcuni fornitori tendono a ignorare completamente segmenti e campi opzionali. Altri potrebbero scegliere di inviare più dati di altri. Ecco perché sono stato costretto a creare validatori per segmenti e campi per verificare se il file fosse corretto o meno.

Puoi immaginare l'incubo delle espressioni regolari che sto avendo adesso. Inoltre, ogni fornitore ha bisogno di molte modifiche alle espressioni regolari che tendo a costruire un parser per ogni fornitore.

Domande:

1- Questa è la procedura migliore per analizzare i file (usando le espressioni regolari)?

2- C'è una soluzione migliore per analizzare i file (forse ci sono soluzioni già pronte in giro)? Sarà in grado di mostrare quale segmento manca o se il file è corrotto?

3- Se devo comunque costruire il mio parser, quale schema o metodologia di progettazione dovrei usare?

Note:

Ho letto da qualche parte su yacc e ANTLR, ma non so se corrispondono ai miei bisogni o no!

    
posta Songo 07.05.2012 - 13:40
fonte

6 risposte

18

Ciò di cui hai bisogno è un vero parser. Le espressioni regolari gestiscono il lexing, non l'analisi. Cioè, identificano i token nel flusso di input. L'analisi è il contesto dei token, I.E. chi va dove e in quale ordine.

Il classico strumento di analisi è yacc / bison . Il lexer classico è lex / flex . Poiché php consente l'integrazione del codice C , puoi usare flex e bison per creare il tuo parser, avere php chiamarlo sull'input file / stream, quindi ottieni i risultati.

Sarà incredibilmente veloce e molto più facile da utilizzare con una volta compresi gli strumenti . Suggerisco di leggere Lex and Yacc 2nd Ed. da O'Reilly. Per un esempio, ho impostato un progetto flex e bison su github , con un makefile. È cross-compilabile per Windows, se necessario.

È è complesso, ma come hai scoperto, ciò che devi fare è complesso. C'è una grande quantità di "cose" che devono essere fatte per un parser correttamente funzionante, e flex e bisonte si occupano dei bit meccanici. Altrimenti, ti trovi nella non invidiabile posizione di scrivere codice allo stesso livello di astrazione dell'assemblaggio.

    
risposta data 07.05.2012 - 15:02
fonte
3

ouch ... parser 'vero'? macchine di stato ??

scusate ma sono stato convertito da accademico in hacker da quando ho iniziato il mio lavoro .. quindi direi che ci sono modi più semplici ... anche se forse non sono "raffinati" dal punto di vista accademico:)

Cercherò di offrire un approccio alternativo che alcuni potrebbero o potrebbero non essere d'accordo, ma può essere molto pratico in un ambiente di lavoro.

Vorrei;

loop every line
   X = pop the first 3 letters of line
   Y = rest of line
   case X = 'UNA':
       class init (Y)

da lì userei le classi per i tipi di dati. separare componenti e separatori di elementi e iterare sugli array restituiti.

Per me, questo è riutilizzo del codice, OO, bassa coesione e altamente modulare .. e facile da debugare e programmare. più semplice è meglio.

per analizzare un file non hai bisogno di macchine di stato o di qualcosa di completamente complicato ... le macchine di stato sono adatte per analizzare il codice, sarai sorpreso di quanto sia potente il codice pseduo sopra riportato quando viene usato in un contesto OO.

ps. ho lavorato con file molto simili prima:)

Altro pseudo-codice postato qui:

classe

UNA:

init(Y):
 remove ' from end
 components = Y.split(':') 
 for c in components
     .. etc..

 getComponents():
   logic..
   return

 getSomethingElse():
   logic..
   return

class UNZ:
   ...

Parser(lines):

Msg = new obj;

for line in lines
   X = pop the first 3 letters of line
   Y = rest of line
   case X = 'UNA':
      Msg.add(UNA(Y))

msg.isOK = true
return Msg

potresti quindi usarlo in questo modo ..

msg = Main(File.getLines());
// could put in error checking
// if msg.isOK:
msg.UNA.getSomethingElse();

e dici che hai più di un segmento .. usa una coda per aggiungerli e ottenere il primo, il secondo ecc. di cui hai bisogno. In realtà stai solo rappresentando il msg in un oggetto e dando ai metodi oggetto di chiamare i dati. potresti trarne vantaggio creando anche metodi personalizzati .. per ereditarietà .. beh questa è una domanda diversa e penso che potresti facilmente applicarla se la capisci

    
risposta data 07.05.2012 - 15:47
fonte
1

Hai provato a cercare su google "PHP EDIFACT"? Questo è uno dei primi risultati che è apparso: link

Anche se potrebbe non essere sufficiente per il tuo caso d'uso, potresti essere in grado di ricavarne alcune idee. Non mi piace il codice con i suoi molti nidificati per loop e condizioni, ma potrebbe essere un inizio.

    
risposta data 08.05.2012 - 11:13
fonte
1

Bene, dato che Yacc / Bison + Flex / Lex è stato menzionato, potrei anche inserire una delle altre alternative principali: i combinatori di parser. Questi sono popolari nella programmazione funzionale come Haskell, ma se puoi interfacciare con il codice C puoi usarli e, cosa sai, qualcuno ne ha scritto uno per PHP anche. (Non ho esperienza con quella particolare implementazione, ma se funziona come la maggior parte di essi, dovrebbe essere abbastanza carino.)

Il concetto generale è che inizi con un set di parser piccoli, facili da definire, in genere tokenizer. Come se avessi una funzione di parser per ciascuno dei 6 elementi di dati che hai menzionato. Quindi usi i combinatori (funzioni che combinano le funzioni) per creare parser più grandi che catturano elementi più grandi. Come un segmento facoltativo sarebbe il combinatore optional che opera sul parser di segmento.

Non sei sicuro di come funzioni correttamente in PHP, ma è un modo divertente per scrivere un parser e mi diverto molto a usarli in altre lingue.

    
risposta data 08.05.2012 - 15:22
fonte
0

invece di manipolare le espressioni regolari crea la tua macchina a stati

questo sarà più leggibile (e sarà in grado di avere commenti migliori) in situazioni non banali e sarà più facile eseguire il debug che la casella nera che è regex

    
risposta data 07.05.2012 - 15:14
fonte
0

Non so cosa vuoi fare esattamente con questi dati in seguito e se non è un maglio per un pazzo, ma ho avuto esperienze positive con eli . Descrivi le frasi lessicali e poi la sintassi concreta / astratta e generi ciò che vuoi generare.

    
risposta data 09.05.2012 - 18:34
fonte

Leggi altre domande sui tag