2011-03-07 16 views
5

Necesito analizar algunas estructuras de datos especiales. Ellos están en un formato un tanto similar a-C que se ve más o menos así:Cómo analizar el código (en Python)?

Group("GroupName") { 
    /* C-Style comment */ 
    Group("AnotherGroupName") { 
     Entry("some","variables",0,3.141); 
     Entry("other","variables",1,2.718); 
    } 
    Entry("linebreaks", 
      "allowed", 
      3, 
      1.414 
     ); 
} 

me ocurren varias maneras de hacer esto. Podría 'tokenize' el código usando expresiones regulares. Pude leer el código de a un personaje a la vez y usar una máquina de estados para construir mi estructura de datos. Podría deshacerme de los saltos de línea de coma y leer el asunto línea por línea. Podría escribir algún script de conversión que convierta este código en código Python ejecutable.

¿Hay una buena manera pitónica de analizar archivos como este?
¿Cómo harías para analizarlo?

Esto es más una pregunta general sobre cómo analizar cadenas y no tanto sobre este formato de archivo en particular.

+3

[Este artículo] (http://nedbatchelder.com/text/python-parsers.html) podría ser de su interés. –

Respuesta

6

Usando pyparsing (Marcos Tolonen, que estaba a punto de hacer clic en "Enviar mensaje" cuando el mensaje llegó a través), esto es bastante sencillo - véanse los comentarios incrustados en el código de abajo:

data = """Group("GroupName") { 
    /* C-Style comment */ 
    Group("AnotherGroupName") { 
     Entry("some","variables",0,3.141); 
     Entry("other","variables",1,2.718); 
    } 
    Entry("linebreaks", 
      "allowed", 
      3, 
      1.414 
     ); 
} """ 

from pyparsing import * 

# define basic punctuation and data types 
LBRACE,RBRACE,LPAREN,RPAREN,SEMI = map(Suppress,"{}();") 
GROUP = Keyword("Group") 
ENTRY = Keyword("Entry") 

# use parse actions to do parse-time conversion of values 
real = Regex(r"[+-]?\d+\.\d*").setParseAction(lambda t:float(t[0])) 
integer = Regex(r"[+-]?\d+").setParseAction(lambda t:int(t[0])) 

# parses a string enclosed in quotes, but strips off the quotes at parse time 
string = QuotedString('"') 

# define structure expressions 
value = string | real | integer 
entry = Group(ENTRY + LPAREN + Group(Optional(delimitedList(value)))) + RPAREN + SEMI 

# since Groups can contain Groups, need to use a Forward to define recursive expression 
group = Forward() 
group << Group(GROUP + LPAREN + string("name") + RPAREN + 
      LBRACE + Group(ZeroOrMore(group | entry))("body") + RBRACE) 

# ignore C style comments wherever they occur 
group.ignore(cStyleComment) 

# parse the sample text 
result = group.parseString(data) 

# print out the tokens as a nice indented list using pprint 
from pprint import pprint 
pprint(result.asList()) 

imprime

[['Group', 
    'GroupName', 
    [['Group', 
    'AnotherGroupName', 
    [['Entry', ['some', 'variables', 0, 3.141]], 
    ['Entry', ['other', 'variables', 1, 2.718]]]], 
    ['Entry', ['linebreaks', 'allowed', 3, 1.4139999999999999]]]]] 

(Desafortunadamente, puede haber algo de confusión, ya que pyparsing define una clase "Grupo", para impartir la estructura de las fichas analizados - Nótese cómo las listas de valores en una entrada quedan agrupados porque la expresión lista está encerrado dentro de un grupo pyparsing .)

+3

¡Acabas de ganar $ 10 en la librería O'Reilly! – bastibe

1

Depende de la frecuencia con que lo necesite y si la sintaxis sigue siendo la misma. Si las respuestas son "muy a menudo" y "más o menos sí", buscaría una forma de expresar la sintaxis y escribir un analizador específico en ese idioma con una herramienta como PyPEG o LEPL. Definir las reglas del analizador es un gran trabajo, así que, a menos que necesite analizar el mismo tipo de archivos a menudo, puede que no sea necesariamente efectivo.

Pero si nos fijamos en la página PyPEG, le indica cómo generar los datos analizados en XML, por lo que si esa herramienta no le da suficiente potencia, puede usarla para generar el XML y luego usar, por ejemplo, lxml para analizar el xml.

Cuestiones relacionadas