Analizar un archivo lisp con Python

Tengo el siguiente archivo lisp, que es de la base de datos de aprendizaje automático de UCI . Me gustaría convertirlo en un archivo de texto plano usando python. Una línea típica se ve así:

(1 ((st 8) (pitch 67) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 12) (pitch 67) (dur 8) (keysig 1) (timesig 12) (fermata 0))) 

Me gustaría analizar esto en un archivo de texto como:

 time pitch duration keysig timesig fermata 8 67 4 1 12 0 12 67 8 1 12 0 

¿Hay un módulo de python para analizar inteligentemente esto? Esta es mi primera vez viendo lisp.

Como se muestra en esta respuesta , pyparsing parece ser la herramienta correcta para eso:

 inputdata = '(1 ((st 8) (pitch 67) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 12) (pitch 67) (dur 8) (keysig 1) (timesig 12) (fermata 0)))' from pyparsing import OneOrMore, nestedExpr data = OneOrMore(nestedExpr()).parseString(inputdata) print data # [['1', [['st', '8'], ['pitch', '67'], ['dur', '4'], ['keysig', '1'], ['timesig', '12'], ['fermata', '0']], [['st', '12'], ['pitch', '67'], ['dur', '8'], ['keysig', '1'], ['timesig', '12'], ['fermata', '0']]]] 

En aras de la integridad, esta es la forma de dar formato a los resultados (utilizando texttable ):

 from texttable import Texttable tab = Texttable() for row in data.asList()[0][1:]: row = dict(row) tab.header(row.keys()) tab.add_row(row.values()) print tab.draw() 
 + --------- + -------- + ---- + ------- + ----- + --------- +
 |  timesig |  llaves |  st |  terreno de juego  dur |  fermata |
 + ========= + ======== + ==== + ======= + ===== + ========= +
 |  12 |  1 |  8 |  67 |  4 |  0 |
 + --------- + -------- + ---- + ------- + ----- + --------- +
 |  12 |  1 |  12 |  67 |  8 |  0 |
 + --------- + -------- + ---- + ------- + ----- + --------- +

Para convertir esos datos de nuevo a la notación lisp:

 def lisp(x): return '(%s)' % ' '.join(lisp(y) for y in x) if isinstance(x, list) else x d = lisp(d[0]) 

Si sabe que los datos son correctos y el formato es uniforme (lo parece a primera vista), y si necesita solo estos datos y no necesita resolver el problema general … entonces, ¿por qué no reemplazar simplemente todos los datos no numéricos? ¿Con un espacio y luego ir con split?

 import re data = open("chorales.lisp").read().split("\n") data = [re.sub("[^-0-9]+", " ", x) for x in data] for L in data: L = map(int, L.split()) i = 1 # first element is chorale number while i < len(L): st, pitch, dur, keysig, timesig, fermata = L[i:i+6] i += 6 ... your processing goes here ... 

Separarlo en pares con una expresión regular:

 In [1]: import re In [2]: txt = '(((st 8) (pitch 67) (dur 4) (keysig 1) (timesig 12) (fermata 0))((st 12) (pitch 67) (dur 8) (keysig 1) (timesig 12) (fermata 0)))' In [3]: [p.split() for p in re.findall('\w+\s+\d+', txt)] Out[3]: [['st', '8'], ['pitch', '67'], ['dur', '4'], ['keysig', '1'], ['timesig', '12'], ['fermata', '0'], ['st', '12'], ['pitch', '67'], ['dur', '8'], ['keysig', '1'], ['timesig', '12'], ['fermata', '0']] 

Luego conviértelo en un diccionario:

 dct = {} for p in data: if not p[0] in dct.keys(): dct[p[0]] = [p[1]] else: dct[p[0]].append(p[1]) 

El resultado:

 In [10]: dct Out[10]: {'timesig': ['12', '12'], 'keysig': ['1', '1'], 'st': ['8', '12'], 'pitch': ['67', '67'], 'dur': ['4', '8'], 'fermata': ['0', '0']} 

Impresión:

 print 'time pitch duration keysig timesig fermata' for t in range(len(dct['st'])): print dct['st'][t], dct['pitch'][t], dct['dur'][t], print dct['keysig'][t], dct['timesig'][t], dct['fermata'][t] 

El formato adecuado se deja como un ejercicio para el lector …

Como los datos ya están en Lisp, usa lisp en sí:

 (let ((input '(1 ((ST 8) (PITCH 67) (DUR 4) (KEYSIG 1) (TIMESIG 12) (FERMATA 0)) ((ST 12) (PITCH 67) (DUR 8) (KEYSIG 1) (TIMESIG 12) (FERMATA 0))))) (let ((row-headers (mapcar 'car (second input))) (row-data (mapcar (lambda (row) (mapcar 'second row)) (cdr input)))) (format t "~{~A~^ ~}~%" row-headers) (format t "~{~{~A~^ ~}~^ ~%~}" row-data)))