2012-12-27 7 views
6

Ich habe die folgende Lisp-Datei, die von der UCI machine learning database ist. Ich möchte es in eine flache Textdatei mit Python konvertieren. Eine typische Zeile sieht wie folgt aus:Parsing einer Lisp-Datei mit Python

(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))) 

Ich möchte diese in eine Textdatei analysieren, wie:

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

Gibt es ein Python-Modul, um diese intelligent zu analysieren? Dies ist mein erstes Mal Lispeln zu sehen.

+0

Does [Parsing S-Ausdrücke in Python] (http://stackoverflow.com/q/3182594) helfen? –

+0

Danke, schau dir das mal an. – qua

+1

Warum lisp nicht verwenden, um es in ein anderes Format zu konvertieren? – Marcin

Antwort

20

Wie in this answer gezeigt, pyparsing erscheint das richtige Werkzeug für das sein:

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']]]] 

Für die Vollständigkeit halber ist dies, wie die Ergebnisse zu formatieren (mit 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 | keysig | st | pitch | dur | fermata | 
+=========+========+====+=======+=====+=========+ 
| 12  | 1  | 8 | 67 | 4 | 0  | 
+---------+--------+----+-------+-----+---------+ 
| 12  | 1  | 12 | 67 | 8 | 0  | 
+---------+--------+----+-------+-----+---------+ 

Um diese Daten zurück in die Lisp-Notation zu konvertieren:

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

d = lisp(d[0]) 
+0

Dies ist definitiv die Antwort, seit die Op nach "einem Python * Modul * um * intelligent * dies zu analysieren" – Bakuriu

+0

Danke! Wirklich hilfreich. – qua

+0

Wie würden Sie die Daten wieder in Lisp-Code konvertieren? –

1

trennen sie in Paaren mit einem regulären Ausdruck:

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']] 

es dann in ein Wörterbuch machen:

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

Das Ergebnis:

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

Druck:

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] 

Der richtige Formatierung ...

für den Leser als Übung
2

Wenn Sie wissen, dass die Daten korrekt sind und das Format einheitlich (scheint so bei einem ersten Blick), und wenn Sie sich nur Daten und don brauchen muss das allgemeine Problem nicht lösen ... dann warum nicht einfach alle nicht-numerischen durch Leerzeichen ersetzen und dann mit split gehen?

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 ... 
1

Da die Daten bereits in Lisp ist, verwenden Sie Lisp selbst:

(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)))