Meilleure façon d'analyser un fichier


9

J'essaie de trouver une meilleure solution pour faire un analyseur à certains des formats de fichiers célèbres tels que: EDIFACT et TRADACOMS .

Si vous n'êtes pas familier avec ces normes, consultez cet exemple de Wikipedia:

Voir ci-dessous pour un exemple de message EDIFACT utilisé pour répondre à une demande de disponibilité de produit: -

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'

Le segment UNA est facultatif. S'il est présent, il spécifie les caractères spéciaux à utiliser pour interpréter le reste du message. Il y a six caractères suivant UNA dans cet ordre:

  • séparateur d'éléments de données de composant (: dans cet exemple)
  • séparateur d'éléments de données (+ dans cet exemple)
  • notification décimale (. dans cet exemple)
  • libérer le caractère (? dans cet exemple)
  • réservé, doit être un espace
  • terminateur de segment ('dans cet exemple)

Comme vous pouvez le voir, ce ne sont que des données formatées d'une manière spéciale qui attendent d'être analysées (un peu comme les fichiers XML ).

Maintenant, mon système est construit sur PHP et j'ai pu créer un analyseur utilisant des expressions régulières pour chaque segment, mais le problème n'est pas que tout le monde implémente parfaitement la norme.

Certains fournisseurs ont tendance à ignorer complètement les segments et champs facultatifs. D'autres peuvent choisir d'envoyer plus de données que d'autres. C'est pourquoi j'ai été obligé de créer des validateurs pour les segments et les champs pour tester si le fichier était correct ou non.

Vous pouvez imaginer le cauchemar des expressions régulières que j'ai en ce moment. De plus, chaque fournisseur a besoin de nombreuses modifications des expressions régulières que j'ai tendance à construire un analyseur pour chaque fournisseur.


Des questions:

1- Est-ce la meilleure pratique pour analyser des fichiers (en utilisant des expressions régulières)?

2- Existe-t-il une meilleure solution pour analyser les fichiers (peut-être existe-t-il une solution toute faite)? Sera-t-il capable de montrer quel segment est manquant ou si le fichier est corrompu?

3- Si je dois quand même construire mon analyseur, quel modèle de conception ou méthodologie dois-je utiliser?

Remarques:

J'ai lu quelque part sur yacc et ANTLR, mais je ne sais pas s'ils correspondent à mes besoins ou non!


Après avoir vu cette grammaire EDIFACT, les analyseurs et les bibliothèques (Java), je me demande si l'utilisation d'un lexer / analyseur fonctionnerait. Si c'était moi, j'essaierais d'abord le combinateur d'analyseur. :)
Guy Coder

Réponses:


18

Ce dont vous avez besoin est un véritable analyseur. Les expressions régulières gèrent le lexing, pas l'analyse. Autrement dit, ils identifient les jetons dans votre flux d'entrée. L'analyse est le contexte des jetons, c'est-à-dire qui va où et dans quel ordre.

L'outil d'analyse classique est yacc / bison . Le lexer classique est lex / flex . Depuis php permet intégrer du code C , vous pouvez utiliser flex et bison pour construire votre analyseur, demander à php de l'appeler sur le fichier / flux d'entrée, puis obtenir vos résultats.

Il sera extrêmement rapide et beaucoup plus facile à travailler une fois que vous aurez compris les outils . Je suggère de lire Lex et Yacc 2nd Ed. d'O'Reilly. Par exemple, j'ai mis en place un projet flex et bison sur github , avec un makefile. Il est compilable en croix pour les fenêtres si nécessaire.

Il est complexe, mais comme vous avez découvert, ce que vous devez faire est complexe. Il y a beaucoup de "trucs" qui doivent être faits pour un analyseur fonctionnant correctement, et flex et bison s'occupent des mors mécaniques. Sinon, vous vous retrouvez dans la position peu enviable d'écrire du code dans la même couche d'abstraction que l'assembly.


1
+1 Excellente réponse, d'autant plus qu'elle est fournie avec un exemple d'analyseur.
Caleb

@caleb merci, je travaille beaucoup avec flex / bison, mais il y a très peu d'exemples décents (lire: complexes). Ce n'est pas le meilleur analyseur de tous les temps, car il n'y a pas beaucoup de commentaires, alors n'hésitez pas à envoyer des mises à jour.
Spencer Rathbun

@SpencerRathbun merci beaucoup pour votre réponse détaillée et votre exemple. Je n'ai aucune connaissance de la terminologie que vous avez mentionnée (yacc / bison, lex / flex, etc.), car mon expérience concerne principalement le développement Web. Est « Lex et Yacc 2nd Ed » suffisante pour moi de comprendre tout et construire un bon analyseur? ou y a-t-il d'autres sujets et documents que je devrais aborder en premier?
Songo

@songo Le livre couvre tous les détails pertinents et est assez court, avec environ 300 pages de taille moyenne. Il ne couvre pas l'utilisation de la conception c ou du langage . Heureusement, il existe de nombreuses références c disponibles, telles que K&R The C Programming Language et vous n'avez pas besoin de concevoir un langage, suivez simplement les normes que vous avez référencées. Veuillez noter qu'il est recommandé de lire de couverture en couverture, car les auteurs mentionneront quelque chose une fois et supposeront que si vous en avez besoin, vous reviendrez et relirez. De cette façon, vous ne manquerez de rien.
Spencer Rathbun

Je ne pense pas qu'un lexer standard puisse gérer les séparateurs dynamiques, ce que la ligne UNA peut spécifier. Vous aurez donc au moins besoin d'un lexer avec des caractères personnalisables à l'exécution pour les 5 séparateurs.
Kevin

3

aïe .. 'vrai' analyseur? machines d'état ??

désolé mais j'ai été converti d'universitaire à un pirate informatique depuis que j'ai commencé mon emploi .. donc je dirais qu'il existe des moyens plus faciles .. bien que peut-être pas aussi `` raffinés '' académiquement :)

J'essaierai de proposer une approche alternative avec laquelle certains peuvent ou non être d'accord mais cela PEUT être très pratique dans un environnement de travail.

Je voudrais;

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

à partir de là, j'utiliserais des classes pour les types de données. séparer les séparateurs de composants et d'éléments et parcourir les tableaux renvoyés.

Pour moi, c'est une réutilisation de code, OO, une faible cohésion et hautement modulaire .. et facile à déboguer et à programmer. plus c'est simple, mieux c'est.

pour analyser un fichier, vous n'avez pas besoin de machines d'état ou de tout ce qui est complètement compliqué.

ps. j'ai déjà travaillé avec des fichiers très similaires :)


Plus de pseudo code posté ici:

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

vous pouvez ensuite l'utiliser comme ça ..

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

et dites que vous avez plus d'un segment .. utilisez une file d'attente pour les ajouter et obtenez le premier, le second, etc. selon vos besoins. Vous représentez vraiment le msg dans un obj et donnez aux méthodes objet d'appeler les données. vous pouvez en profiter en créant également des méthodes personnalisées .. pour l'héritage .. eh bien c'est une question différente et je pense que vous pourriez facilement l'appliquer si vous la comprenez


3
Je l'ai déjà fait auparavant, et j'ai trouvé que c'était insuffisant pour autre chose qu'un ou deux cas recognize X token and do Y. Il n'y a pas de contexte, vous ne pouvez pas avoir plusieurs états, dépasser un nombre trivial de cas alourdit le code et la gestion des erreurs est difficile. Je trouve que j'ai eu besoin de ces fonctionnalités dans le monde réel dans presque tous les cas. Cela laisse de côté des erreurs à mesure que la complexité augmente. La partie la plus difficile est de mettre en place un squelette et d'apprendre comment l'outil fonctionne. Dépassez cela et il est tout aussi rapide de préparer quelque chose.
Spencer Rathbun

c'est un message, de quels états avez-vous besoin? il semblerait qu'un tel message, organisé en une structure de composites et de segments, convienne parfaitement à cette approche OO. la gestion des erreurs se fait par classe et correctement, vous pouvez construire un analyseur très efficace et extensible. des messages comme celui-ci se prêtent aux classes et aux fonctions, en particulier lorsque plusieurs fournisseurs envoient différentes versions du même format. Un exemple serait une fonction dans une classe UNA qui a renvoyé une valeur particulière pour un fournisseur spécifique.
Ross

@Ross donc en gros , vous aurez une « classe UNA » pour le segment « UNA » et à l' intérieur il y aura une méthode parse pour chaque fournisseur ( parseUNAsegemntForVendor1(), parseUNAsegemntForVendor2(), parseUNAsegemntForVendor3(), ... etc), non?
Songo

2
@Ross Il y a des sections dans le message, valables à différents moments de l'analyse. Ce sont les États dont je parlais. La conception OO est intelligente et je ne dis pas que cela ne fonctionnera pas . Je pousse le flex et le bison parce que, comme les concepts de programmation fonctionnelle, ils conviennent mieux que d'autres outils, mais la plupart des gens pensent qu'ils sont trop compliqués pour déranger l'apprentissage.
Spencer Rathbun

@Songo .. non, vous analyseriez indépendamment du fournisseur (sauf si vous nouveau qui). l'analyse serait dans l'INIT de la classe. Vous transformez votre message en un objet de données basé sur les mêmes règles utilisées pour construire le message. Si vous aviez besoin de récupérer quelque chose dans le message ... et qu'il soit représenté différemment selon vos fournisseurs, alors vous auriez les différentes fonctions oui ... Mais pourquoi le faire comme ça? utiliser une classe de base et avoir une classe distincte pour chaque fournisseur, remplaçant uniquement lorsque cela est nécessaire, beaucoup plus facile. profiter de l'héritage.
Ross

1

Avez-vous essayé de googler pour "PHP EDIFACT"? C'est l'un des premiers résultats qui est apparu: http://code.google.com/p/edieasy/

Bien que cela ne soit pas suffisant pour votre cas d'utilisation, vous pourrez peut-être en tirer quelques idées. Je n'aime pas le code avec ses nombreuses boucles et conditions imbriquées, mais cela peut être un début.


1
J'ai vérifié de nombreux projets, mais le problème résidait principalement dans les différentes implémentations des fournisseurs utilisant la norme. Je peux forcer un fournisseur à m'envoyer un certain segment, mais je peux le considérer comme facultatif pour un autre fournisseur. C'est pourquoi je vais probablement avoir besoin de construire mon propre analyseur personnalisé de toute façon.
Songo

1

Eh bien, depuis que Yacc / Bison + Flex / Lex ont été mentionnés, je pourrais aussi bien utiliser l'une des autres alternatives majeures: les combinateurs d'analyseurs. Ceux-ci sont populaires dans la programmation fonctionnelle comme avec Haskell, mais si vous pouvez vous interfacer avec du code C, vous pouvez les utiliser et, que savez-vous, quelqu'un en a également écrit un pour PHP. (Je n'ai aucune expérience avec cette implémentation particulière, mais si cela fonctionne comme la plupart d'entre eux, cela devrait être plutôt sympa.)

Le concept général est que vous commencez avec un ensemble de petits analyseurs faciles à définir, généralement des tokeniseurs. Comme si vous aviez une fonction d'analyseur pour chacun des 6 éléments de données que vous avez mentionnés. Ensuite, vous utilisez des combinateurs (fonctions qui combinent des fonctions) pour créer de plus grands analyseurs qui saisissent des éléments plus grands. Comme un segment facultatif, le optionalcombinateur opère sur l'analyseur de segment.

Je ne sais pas si cela fonctionne bien en PHP, mais c'est une façon amusante d'écrire un analyseur et j'aime beaucoup les utiliser dans d'autres langues.


0

au lieu de jouer avec les regex, créez votre propre machine d'état

ce sera plus lisible (et pourra avoir de meilleurs commentaires) dans des situations non triviales et sera plus facile à déboguer que la boîte noire qui est regex


5
Un petit mot, c'est ce que font le flex et le bison sous le capot. Seulement, ils le font bien .
Spencer Rathbun

0

Je ne sais pas ce que vous voulez faire exactement avec ces données par la suite et si ce n'est pas un marteau pour un écrou, mais j'ai eu de bonnes expériences avec eli . Vous décrivez les phrases lexicales puis la syntaxe concrète / abstraite et générez ce que vous voulez générer.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.