Eigener Parser will nicht so wie ich

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Hi.
Ich habe schon des öfteren Parser für Dateien geschrieben, deren Ebenen aber begrenzt waren (Beispiel: INI -> begrenzt, XML -> unbegrenzt).
Nun wollte ich für mein neues Projekt auch einen Parser schreiben, der unbegrenzt viele Ebenen unterstützt, das ganze ist jedoch schwieriger als zuvor vermutet.

Diesen Code hier habe ich bereits fabriziert:

Code: Alles auswählen

self.data = {}

#...

def isNextLineSectionStart(index):
	if len(lines) > index + 1:
		return lines[index + 1].startswith("{")
	
	return False

fileobj = open(path, "r")
lines = [line.strip().rstrip().split("//")[0] for line in filter(lambda line: len(line.strip().rstrip()) and not line.strip().startswith("//"), fileobj)]
fileobj.close()

current = self.data
previous = [self.data]

for index, line in enumerate(lines):
	if isNextLineSectionStart(index):
		previous.append(current)
		current = current[line] = {}
		
		continue
	
	if line.startswith("}"):
		current = previous.pop()
		
		continue
	
	if line.count(" "):
		char = line.find(" ")
		value = line[char + 1:]
		current[line[:char]] = (value[value.find("\"") + 1:value.rfind("\"")] if value.count("\"") else value)

self.data = current
Das ganze soll für folgendes Format funktionieren:

Code: Alles auswählen

start
{
	position1 "-1022.963745,793.409607,8.966581"
	position2 "-1024.971558,70.542549,416.005066"
	name "Start"
	reset 1
	time 0.0
	active 1
	give
	{
		item "flashlight"
		weapon "rpg"
		ammo
		{
			amount 20
			type "rocket"
		}
	}
}

bla
{
	# ...
}
Das ganze funktioniert anscheinend auch, debugging-Ausgaben zeigen genau das an, was es soll (z.B. Zeile 33), aber am Ende ist ``current`` bzw. ``self.data`` leer. Ich habe schon verdammt viel herumprobiert, aber ich kriege das einfach nicht hin.

Ich könnte es auch auf eine Ebene begrenzen, aber das ist
a) nicht der Sinn da ich die Konfiguration dann umschreiben müsste
b) doof weil ich es dann nie verstehen würde

Hoffentlich könnt ihr mir dazu Tipps geben und auch sagen, wieso das ganze so nicht funktioniert. Ich möchte es nämlich nicht nur fertig haben sondern auch verstehen :)

Danke im voraus an alle. Und bitte keine Ratschläge für irgendwelche XML-Parser oder so - damit habe ich bereits gearbeitet aber ich möchte es einfach mal selber probieren ;)

Edit: Mir ist gerade noch eingefallen, dass ich auch "Probleme" beim erkennen bzw. splitten der Daten habe. Bisher kann man nur

Code: Alles auswählen

name "wert"
schreiben - 2 oder mehr Leerzeichen und/oder Tabs kann man nicht benutzen da es dann nicht mehr funktionieren würde. Wie kann man das lösen? (Betrifft Zeile 30 und 31)

Gruß
Grüßle.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Beschränke es auf eine Ebene und rufe dich selbst einfach rekursiv auf :)
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Hi.
Der Kram mit rekursiv ist mir irgendwie zu kompliziert. Ich habs versucht, aber das endete im Chaos :lol:
Habe es nun doch lösen können. Auch das Problem mit dem Leerzeichen ist behoben, wenn auch hässlich.

http://pastebin.com/d7ed2c028

Gruß
Grüßle.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Ist das Datenformat vor gegeben? Wenn nicht, würde ich dir dringend raten, ein existierendes Format (z.B. JSON oder XML) zu nehmen.
Bottle: Micro Web Framework + Development Blog
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Das Datenformat ist nicht vorgegeben aber bei Source-Engine Gameservern durch die KeyValues von VALVe sehr üblich, deshalb wollte ich das Format immitieren.

http://developer.valvesoftware.com/wiki/KeyValues

Gruß
Grüßle.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Siehe http://vimeo.com/8297457 :)

Ich habe mir eine Screencasting-Software gekauft und das war mein erster Versuch. Vielleicht hilft's ja aber trotzdem...

Stefan
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

lol :D
Leider bist du verdammt leise, muss die Boxen hochdrehen - dann geht's.

Edit: Hab's mir mal reingezogen. Viel einfacher als mein Code rumgewurschtel :lol: Weiter so ;)

Edit2: Wenn ich mal fragen darf - was ist das für 'ne nette Testsuite die du da hast? Vermutlich ist das Mac oder Linux - gibt es vergleichbares für Win?

Gruß
Grüßle.
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

sma benutzt Mac.

Respekt, die Stimme ist deutlich und alles verständlich.
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jbs hat geschrieben:Respekt, die Stimme ist deutlich und alles verständlich.
Ja, ist mir auch positiv aufgefallen. sma, hast du das nachsynchronisiert oder so in einem Durchlauf gemacht?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Leonidas hat geschrieben:
jbs hat geschrieben:Respekt, die Stimme ist deutlich und alles verständlich.
Ja, ist mir auch positiv aufgefallen. sma, hast du das nachsynchronisiert oder so in einem Durchlauf gemacht?
Aufgrund seines Kommentars ist das vermutlich nicht nachsynchronisiert ;)
Dies ist mein erstes Video und ich fand es verdammt schwer, gleichzeitig zu tippen und dabei zu reden.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ja, ich benutze einen Mac. Ich glaube auch nicht, das ich daraus in Vergangenheit wirklich ein Geheimnis gemacht habe ;) Die "Testsuite" ist einfach die "Run Script"-Funktion von TextMate, dem Texteditor, den ich für Python benutze. Das ich leise bin, ist mir leider auch aufgefallen - allerdings erst in der Vimeo-Version. Man kann da auch noch die Original-Quicktime-Datei laden. Das Video habe ich in einem Durchlauf aufgezeichnet, allerdings seht ihr da den dritten Versuch.

Stefan
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sma hat geschrieben:Man kann da auch noch die Original-Quicktime-Datei laden. Das Video habe ich in einem Durchlauf aufgezeichnet, allerdings seht ihr da den dritten Versuch.
Hab ich auch gemacht (Chromium spielt die sogar direkt ab) und mich gewundert was die anderen mit "zu leise" meinten.

@ice2k3: Ooops.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

sma hat geschrieben:Siehe http://vimeo.com/8297457 :)
Ganz großes Kino!

Schöne Lösung, schöner Screencast.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

sma, du hast nen neuen Fan :D Mehr davon :)
Bottle: Micro Web Framework + Development Blog
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ein paar Sachen, diem mir aufgefallen sind

wieso verwendest du in

Code: Alles auswählen

print parse(iter(example.splitlines()))
das iter()?
Splitlines gibt doch eine Liste zurück

Zeile 27 und 28 könntest du auf

Code: Alles auswählen

key, value = line.split(None, 1)
kürzen

Das Video ist wirklich gut gelungen
the more they change the more they stay the same
Benutzeravatar
martin101986
User
Beiträge: 85
Registriert: Montag 3. Dezember 2007, 19:15
Wohnort: Steiermark, Österreich

Hallo,
Dav1d hat geschrieben:Ein paar Sachen, diem mir aufgefallen sind

wieso verwendest du in

Code: Alles auswählen

print parse(iter(example.splitlines()))
das iter()?
Splitlines gibt doch eine Liste zurück
Das iter() hat hier schon seinen Sinn. Wenn du der Funktion parse nur eine Liste übergibst, erreichst du die maximale Rekursionstiefe da du innerhalb der von parse die Funktion parse mit der selben Liste wieder aufruft und diese wieder von vorne abgearbeitet wird und dann wieder parse aufruft usw, es wird niemals die Abbruchbedingung erfüllt.

Verwendest du hingegen einen Iterator wird die next()-Methode des Iterators aufgerufen. Wenn du innerhalb von parse erneut parse aufrufst weiß der Iterator an welcher Stelle er stehen geblieben ist und gibt den nächsten Wert zurück.

Hoffe das hilft dir weiter.

Grüße Martin
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Danke,

das habe ich total übersehen
the more they change the more they stay the same
fred.reichbier
User
Beiträge: 155
Registriert: Freitag 29. Dezember 2006, 18:27

Ich kann mich nur anschließen. Sehr schönes Video! :)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich fand das Video (und die anderen!) auch sehr gut gemacht. Daher konnte ich nicht umhin, das ganze ein wenig zu erweitern und zu ergänzen. Das Ergebnis findet man hier: Source

Ich war so frei smas Funtkion ein wenig aufzubohren - zumindest habe ich mal vermutet, dass auch Interesse am Parsen der Koordinaten bestehen könnte.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Hyperion hat geschrieben:Ich war so frei smas Funtkion ein wenig aufzubohren - zumindest habe ich mal vermutet, dass auch Interesse am Parsen der Koordinaten bestehen könnte.
Deine Koordinaten-Erkennung würde den String "Web 2.0" zu [2.0] machen. Ist das beabsichtigt? Wenn du Koordinaten erkennen willst, würde ich auf einen String der Form \d+\.\d+,\d+\.\d+,\d+\.\d+ achten und nur diesen umwandeln - wenn überhaupt, da das schon sehr speziell ist und dem Parser die Allgemeingültigkeit nimmt.

Stefan
Antworten