La mejor manera de analizar un archivo

9

Estoy tratando de encontrar una mejor solución para hacer un analizador sintáctico de algunos de los formatos de archivo famosos, como: EDIFACT y TRADACOMS .

Si no está familiarizado con estos estándares, consulte este ejemplo de Wikipedia:

Consulte a continuación un ejemplo de un mensaje EDIFACT utilizado para responder a una solicitud de disponibilidad del producto:

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'

El segmento UNA es opcional. Si está presente, especifica los caracteres especiales que se utilizarán para interpretar el resto del mensaje. Hay seis caracteres que siguen a UNA en este orden:

  • separador de elementos de datos de componentes (: en esta muestra)
  • separador de elementos de datos (+ en esta muestra)
  • notificación decimal (. en esta muestra)
  • liberar personaje (? en esta muestra)
  • reservado, debe ser un espacio
  • terminador de segmento ('en esta muestra)

Como puede ver, solo se trata de algunos datos formateados de una manera especial que esperan ser analizados (al igual que los archivos XML ).

Ahora mi sistema está construido en PHP y pude crear un analizador utilizando expresiones regulares para cada segmento, pero el problema no es que todos implementen el estándar a la perfección.

Algunos proveedores tienden a ignorar por completo los segmentos y campos opcionales. Otros pueden optar por enviar más datos que otros. Es por eso que me vi obligado a crear validadores para segmentos y campos para probar si el archivo era correcto o no.

Puedes imaginar la pesadilla de las expresiones regulares que estoy teniendo en este momento. Además, cada proveedor necesita muchas modificaciones a las expresiones regulares que tiendo a construir un analizador para cada proveedor.


Preguntas:

1- ¿Es esta la mejor práctica para analizar archivos (usando expresiones regulares)?

2- ¿Existe una mejor solución para analizar archivos (tal vez hay una solución preparada)? ¿Podrá mostrar qué segmento falta o si el archivo está dañado?

3- Si tengo que construir mi analizador de todos modos, ¿qué patrón de diseño o metodología debo usar?

Notas:

Leí en alguna parte sobre yacc y ANTLR, ¡pero no sé si satisfacen mis necesidades o no!

Songo
fuente
Después de ver esta gramática, analizadores y bibliotecas EDIFACT (Java), me pregunto si funcionaría un lexer / analizador. Si fuera yo, primero probaría el combinador de analizador sintáctico. :)
Guy Coder

Respuestas:

18

Lo que necesitas es un verdadero analizador. Las expresiones regulares manejan lexing, no análisis. Es decir, identifican tokens dentro de su flujo de entrada. El análisis es el contexto de los tokens, es decir, quién va a dónde y en qué orden.

La herramienta de análisis clásica es yacc / bison . El lexer clásico es lex / flex . Como php permite la integración del código C , puede usar flex y bison para construir su analizador, hacer que php lo llame en el archivo / flujo de entrada y luego obtenga sus resultados.

Será increíblemente rápido y será mucho más fácil trabajar con él una vez que comprenda las herramientas . Sugiero leer Lex y Yacc 2nd Ed. de O'Reilly. Por ejemplo, he configurado un proyecto flex y bison en github , con un archivo MAKE. Es de compilación cruzada para Windows si es necesario.

Que es compleja, pero a medida que se enteraron, lo que necesita hacer es compleja. Hay una gran cantidad de "cosas" que se deben hacer para que un analizador funcione correctamente, y el trato flexible y bisonte con las brocas mecánicas. De lo contrario, se encontrará en la posición poco envidiable de escribir código en la misma capa de abstracción que el ensamblado.

Spencer Rathbun
fuente
1
+1 Gran respuesta, especialmente teniendo en cuenta que viene con un analizador de muestra.
Caleb
@caleb gracias, trabajo mucho con flex / bison, pero hay muy pocos ejemplos decentes (léase: complejos). Este no es el mejor analizador, ya que no hay muchos comentarios, así que no dude en enviar actualizaciones.
Spencer Rathbun
@SpencerRathbun muchas gracias por su respuesta detallada y ejemplo. No tengo conocimiento de la terminología que mencionaste (yacc / bison, lex / flex, ... etc.) Ya que mi experiencia es principalmente sobre desarrollo web. ¿Es "Lex and Yacc 2nd Ed" suficiente para que yo entienda todo y construya un buen analizador? ¿O hay otros temas y materiales que debería cubrir primero?
Songo
@songo El libro cubre todos los detalles relevantes y es bastante corto, registrando ~ 300 páginas de tamaño medio. No cubre el uso de c o diseño de lenguaje . Afortunadamente, hay muchas referencias c disponibles, como K&R The C Programming Language y no necesita diseñar un lenguaje, solo siga los estándares a los que ha hecho referencia. Tenga en cuenta que se recomienda leer de principio a fin, ya que los autores mencionarán algo una vez, y suponen que si lo necesita, volverá a leerlo. De esa manera no te pierdes nada.
Spencer Rathbun
No creo que un lexer estándar pueda manejar separadores dinámicos, que la línea UNA puede especificar. Por lo menos, necesitará un lexer con caracteres personalizables en tiempo de ejecución para los 5 separadores.
Kevin
3

ouch .. 'verdadero' analizador? máquinas de estado?

lo siento, pero me convertí de académico a pirata informático desde que comencé mi empleo ... por lo que diría que hay formas más fáciles ... aunque tal vez no sea 'refinado' académicamente :)

Intentaré ofrecer un enfoque alternativo con el que algunos puedan o no estar de acuerdo, pero PUEDE ser muy práctico en un entorno de trabajo.

Me gustaría;

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

a partir de ahí usaría clases para los tipos de datos. dividir separadores de componentes y elementos e iterar sobre las matrices devueltas.

Para mí, esto es reutilización de código, OO, baja cohesión y altamente modular ... y fácil de depurar y programar. Más simple es mejor.

para analizar un archivo no necesita máquinas de estado ni nada completamente complicado ... las máquinas de estado son muy adecuadas para analizar el código, se sorprenderá de lo poderoso que puede ser el código pseduo anterior cuando se usa en un contexto OO.

PD. He trabajado con archivos muy similares antes :)


Más pseudocódigo publicado aquí:

clase

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

entonces podrías usarlo así ...

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

y digamos que tiene más de un segmento ... use una cola para agregarlos y obtenga el primero, segundo, etc. según lo necesite. Realmente solo representa el mensaje en un obj y le da a los objetos métodos para llamar a los datos. podría aprovechar esto creando también métodos personalizados ... para la herencia ... bueno, esa es una pregunta diferente y creo que podría aplicarla fácilmente si la entiende

Ross
fuente
3
Yo he hecho antes, y nos pareció que es insuficiente para nada más allá de uno o dos casos de recognize X token and do Y. No hay contexto, no puede tener múltiples estados, pasar un número trivial de casos hincha el código y el manejo de errores es difícil. Creo que he necesitado estas características en el mundo real en casi todos los casos. Eso deja de lado los errores a medida que crece la complejidad. La parte más difícil es configurar un esqueleto y aprender cómo funciona la herramienta. Supera eso y es igual de rápido preparar algo.
Spencer Rathbun
es un mensaje, ¿qué estados necesitas? Parecería que tal mensaje, que está organizado en una estructura de compuestos y segmentos, encajaría perfectamente en este enfoque OO. el manejo de errores se realiza por clase y correctamente, puede construir un analizador que sea muy eficiente y extensible. mensajes como este se prestan a clases y funciones, especialmente cuando varios proveedores envían diferentes tipos del mismo formato. Un ejemplo sería una función en una clase UNA que devolvió un valor particular para un proveedor específico.
Ross
@Ross así que básicamente tendrá una "clase de UNA" para el segmento "una" y en su interior habrá un método de análisis para cada proveedor ( parseUNAsegemntForVendor1(), parseUNAsegemntForVendor2(), parseUNAsegemntForVendor3(), ... etc), ¿verdad?
Songo
2
@Ross Hay secciones para el mensaje, válidas en diferentes puntos durante el análisis. Esos son los estados de los que estaba hablando. El diseño OO es inteligente, y no digo que no funcione . Empujo flex y bison porque, como los conceptos de programación funcional, se adaptan mejor que otras herramientas, pero la mayoría de las personas creen que son demasiado complicados para molestarse en aprender.
Spencer Rathbun
@Songo ... no, usted analizaría independientemente del proveedor (a menos que sepa quién). el análisis estaría en el INIT de la clase. Convierte tu mensaje en un objeto de datos basado en las mismas reglas utilizadas para construir el mensaje. Sin embargo, si necesita obtener algo del mensaje ... y está representado de manera diferente entre sus proveedores, entonces tendría las diferentes funciones, sí ... Pero, ¿por qué es así? use una clase base y tenga una clase separada para cada proveedor, anulando solo cuando sea necesario, mucho más fácil. aprovecha la herencia.
Ross
1

¿Has intentado buscar en Google "PHP EDIFACT"? Este es uno de los primeros resultados que apareció: http://code.google.com/p/edieasy/

Si bien puede no ser suficiente para su caso de uso, es posible que pueda obtener algunas ideas. No me gusta el código con sus muchos bucles y condiciones anidados, pero puede ser un comienzo.

chiborg
fuente
1
Revisé muchos proyectos, pero el problema estaba principalmente en las diferentes implementaciones de los proveedores que usaban el estándar. Podría obligar a un proveedor a enviarme un segmento determinado, pero puedo considerarlo opcional para otro proveedor. Es por eso que probablemente necesite construir mi propio analizador personalizado de todos modos.
Songo
1

Bueno, desde que se mencionó a Yacc / Bison + Flex / Lex, también podría incluir una de las otras alternativas principales: los combinadores de analizador sintáctico. Estos son populares en la programación funcional como con Haskell, pero si puede interactuar con el código C, puede usarlos y, qué sabe, alguien también escribió uno para PHP. (No tengo experiencia con esa implementación en particular, pero si funciona como la mayoría de ellos, debería ser bastante agradable).

El concepto general es que comience con un conjunto de analizadores pequeños y fáciles de definir, generalmente tokenizadores. Como si tuviera una función de analizador para cada uno de los 6 elementos de datos que mencionó. Luego, usa combinadores (funciones que combinan funciones) para crear analizadores más grandes que agarran elementos más grandes. Como un segmento opcional sería el optionalcombinador que opera en el analizador de segmentos.

No estoy seguro de qué tan bien funciona en PHP, pero es una forma divertida de escribir un analizador y disfruto mucho usarlo en otros idiomas.

CodexArcanum
fuente
0

en lugar de jugar con expresiones regulares, crea tu propia máquina de estados

esto será más legible (y podrá tener mejores comentarios) en situaciones no triviales y será más fácil de depurar que el recuadro negro que es regex

monstruo de trinquete
fuente
55
Una nota rápida, esto es lo que flex y bison hacen debajo del capó. Solo ellos lo hacen bien .
Spencer Rathbun
0

No sé qué quieres hacer exactamente con estos datos después y si no es un mazo para un loco, pero tuve buenas experiencias con Eli . Describe las frases léxicas y luego la sintaxis concreta / abstracta y genera lo que desea generar.

Sebastian Bauer
fuente