Hilfe zu regulären Ausdrücken / regex

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.
chrave
User
Beiträge: 8
Registriert: Freitag 25. September 2009, 18:42

Nachdem ich mich nun eingearbeitet habe und meine Programme größtenteils laufen bin ich auf ein Problem gestoßen. Ich möchte folgendes Schema matchen:

Code: Alles auswählen

Vorname: Peter
Nachname: Muster
Straße: Königsallee
Stadt: Düsseldorf
Oft ist es leider so, dass sowas in den Dateien steht:

Code: Alles auswählen

Vorname: Hans
Werner
Nachname: Muster
sdkgjals
Straße: Kurfürstendamm
Stadt: Berlin
Ich möchte aber nur das haben, was hinter "Vorname: " usw. steht. Ich kann die Sachen nicht einzeln abfragen(Also jeweils Vorname, Name, Straße, Ort), da ich nur dann einen Treffer haben möchte wenn die 4 nacheinander vorkommen, manchmal steht nämlich fälschlicherweise ein einzelner Nachname in der Textdatei.

Mein regulärer Ausdruck sieht so aus:

Code: Alles auswählen

it = re.finditer(r"Vorname: (.*?)\n*Nachname: (.*?)\nStraße: (.*?)\nStadt: (.*?)\n", input)
Wenn ich jetzt mit Option ".dotall" aufrufe findet er zwar auch die Tupel, welche Fierlefanz dazwischen haben, aber eben mit diesem :(. Ich habe gedacht ich kann irgendwie sagen, dass danach 1 bis 3 newline-Zeichen kommen mit beliebigem Text dazwischen aber irgendwie kriege ich das nicht hin.

Kann mir jemand helfen? Ich möchte also von dem Beispiel mit Fierlefanz

(Hans, Muster, Kurfürstendamm, Berlin) herauskriegen
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Kümmer dich weniger um die Details, schau dir die Struktur als ganzes an.

http://bpaste.net/show/337/

Meine Lösung erlaubt auch einen Doppelpunkt gefolgt von jedem beliebigem Zeichen außer einem Leerzeichen in der Definition, dass könnte eventuell stören.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Ich finde re hier komplett überflüssig:

Code: Alles auswählen

for line in user_input.splitlines():
  try:
    key, value = [x.strip() for x in line.split(':',1)]
    print "Data:", key, "->", value
  except ValueError:
    print "Garbage:", line
Bottle: Micro Web Framework + Development Blog
chrave
User
Beiträge: 8
Registriert: Freitag 25. September 2009, 18:42

Hallo,

danke für die schnelle Antwort.Leider geht das nicht ganz. Das Problem ist, dein Ausdruck matcht auch

Code: Alles auswählen

Nachname: 
Straße: 
Ich hatte extra die 4 Sachen hingeschrieben, damit nur Tupel gefunden werden, die komplett sind. Dazu gibt dieser Ausdruck auch leider "Nachname: " und "Straße: " raus. Das hatte ich bei mir extra vermieden und den Ausdruck so penibel gemacht.
chrave
User
Beiträge: 8
Registriert: Freitag 25. September 2009, 18:42

Defnull hat geschrieben:Ich finde re hier komplett überflüssig:

Code: Alles auswählen

for line in user_input.splitlines():
  try:
    key, value = [x.strip() for x in line.split(':',1)]
    print "Data:", key, "->", value
  except ValueError:
    print "Garbage:", line
Das hat schon seinen Sinn, es gibt sehr viele Dateien die ich für die Uni durchforsten muss und da steht nicht nur sowas drin, ich will nur erstmal alles finden was in dieser Struktur ist.

:(
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Meine Güte, dann testest du halt die Werte auf Richtigkeit, bevor du sie verarbeitest. Das musst du eh tun, damit die User keinen Mist ein geben. Ich dachte eigentlich, das wäre selbsterklärend.

Code: Alles auswählen

for line in user_input.splitlines():
  try:
    key, value = [x.strip() for x in line.split(':',1)]
    if key in ('Vorname','Nachname','Straße','Stadt') and len(value) > 0:
      print "Data:", key, "->", value
  except ValueError:
    print "Garbage:", line
Der Punkt ist: RegExp sind hier nicht notwendig und unnötig kompliziert. Mit der iterativen Variante kannst du ausserdem sinnvolle Fehlermeldungen wie "Key XYZ unbekannt" oder "Key 'Vorname' fehlt" generieren.
Bottle: Micro Web Framework + Development Blog
chrave
User
Beiträge: 8
Registriert: Freitag 25. September 2009, 18:42

Es hat schon seinen Grund warum das nicht geht:

Es wird Folgendes gematcht

Code: Alles auswählen

Nachname: Müller
Straße: Musterstraße.

Es geht hier auch nicht um Usereingaben, sondern zum Teil inkonsistente Textdateien. Mir sind einzeln herumstehende (falsche) Sachen halt schlicht egal. Ich brauche nur solche, die richtig sind. Dazu soll eine andere Regex später auch andere Gebilde treffen, die über mehrere Zeilen gehen und zwingend bestimmte Zeilenanfänge besitzen.

Fällt mir nicht ganz so leicht das hier verständlich darzulegen. Sorry.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

chrave hat geschrieben:danke für die schnelle Antwort.Leider geht das nicht ganz. Das Problem ist, dein Ausdruck matcht auch[...]
Dann verwende statt "*" "+" und es muss mindestens ein Zeichen auftauchen.

Letztendlich musst du sowieso prüfen dass nur bestimmte Schlüssel, auftauchen und die Werte den Kriterien des zugewiesenen Schlüssels entsprechen. Darauf sollte man aber auch selbstständig kommen...
chrave
User
Beiträge: 8
Registriert: Freitag 25. September 2009, 18:42

DasIch hat geschrieben:
chrave hat geschrieben:danke für die schnelle Antwort.Leider geht das nicht ganz. Das Problem ist, dein Ausdruck matcht auch[...]
Dann verwende statt "*" "+" und es muss mindestens ein Zeichen auftauchen.

Letztendlich musst du sowieso prüfen dass nur bestimmte Schlüssel, auftauchen und die Werte den Kriterien des zugewiesenen Schlüssels entsprechen. Darauf sollte man aber auch selbstständig kommen...
Sorry mir raucht nach 3 Tagen fast nonstop coden etwas der Kopf.
Ich bin bisher halt andersrum vorgegangen. Eben den regulären Ausdruck so genau machen dass nur die richtigen Dinge treffen und diese direkt wegschreiben. Das klappt bis auf den Fall dass, durch Formatierungsfehler in den Daten, in der Zeile nach "Vorname: " irgend ein Mist steht. Ich glaube ich werde dann einfach auf diese verzichten. Wenn ich noch prüfe usw kriege ich Laufzeitprobleme.

Danke erstmal für die Hilfe, ich mache morgen weiter.
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Was spricht denn gegen Defnulls Lösung?

re muss sich auch die falschen Zeilen anschauen und ich glaube nicht, dass ein re schneller ist als ein Iterieren über die Zeilen.
[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]
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Statt über Sinn und Zweck von regulären Ausdrücken zu streiten, kann man auch einfach einen passenden Ausdruck bauen:

Code: Alles auswählen

re.findall(ur"Vorname: (.*)\n(?:.*\n)*?Nachname: (.*)\n(?:.*\n)*?Straße: (.*)\n(?:.*\n)*?Stadt: (.*)", s)
Dieser Ausdruck finden passende 4-Tupel, die von beliebig vielen nicht passenden Zeilen unterbrochen sein können. Ich benutze `(?: ... )` damit diese Zeilen nicht als Gruppe zählen und `*?` damit keine passenden Zeilen von `(?:.*\n)` getroffen werden. Ich benutze Unicode, damit ich keine Probleme mit dem "ß" habe. Auch die Eingabe (die nicht "input" heißen sollte, weil das ein reservierter Funktionsname ist) sollte ein Unicode-String sein.

Stefan
chrave
User
Beiträge: 8
Registriert: Freitag 25. September 2009, 18:42

Danke. Ich hatte über's Wochenende noch eine eigene Lösung gefunden

Code: Alles auswählen

re.findall(r"Vorname: (.*)\n+.*\n*Nachname2: (.*)\n+.*\n*Straße: (.*)\n+.*\n*Stadt: (.*)\n", eingang)
Diese ist aber ungeschickt, wie ich gemerkt habe. (?:.*\n) ist deutlich besser als neue Zeile mit beliebigem Inhalt immer manuell hinzuschreiben.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Etwas länger, aber IMHO lesbarer. Was aber auch daran liegen könnte, dass ich mich noch nie mit regulären Ausdrücken anfreunden konnte.

Code: Alles auswählen

In [19]: s = """
   ....: Vorname: Hans
   ....: Werner
   ....: Nachname: Muster
   ....: sdkgjals
   ....: Straße: Kurfürstendamm
   ....: Stadt: Berlin
   ....: """

In [20]: filter(bool, (''.join(line.split(':')[1:]).strip() for line in s.splitlines()))
Out[20]: ['Hans', 'Muster', 'Kurf\xc3\xbcrstendamm', 'Berlin']
Huch, ich hab die ganzen weiteren Beiträge völlig übersehen. :oops:
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Wozu die zusätzliche Klammerung im zweiten Argument von filter?
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Code: Alles auswählen

In [21]: filter(bool, ''.join(line.split(':')[1:]).strip() for line in s.splitlines())
------------------------------------------------------------
   File "<ipython console>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument (<ipython console>, line 1)
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Ah, da kriegt der Parser wohl Probleme.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und um es *noch* lesbarer zu machen... ;)

Code: Alles auswählen

In [42]: dict((parts[0].strip(), parts[1].strip()) for parts in (line.split(':', 1) for line in s.splitlines() if ':' in line))
Out[42]: 
{'Nachname': 'Muster',
 'Stadt': 'Berlin',
 'Stra\xc3\x9fe': 'Kurf\xc3\xbcrstendamm',
 'Vorname': 'Hans'}
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Da das aber das einzige Argument ist, sollte ein äußeres Klammerpaar nun reichen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Falls du mich meinst: Nö, das Entfernen des äußeren Klammernpaars erzeugt immer noch einen SyntaxError. Ist aber eigentlich auch richtig so. Man muss sich nur mal angucken, welches Klammernpaar jetzt wohin gehört. Da sind ist kein zusätzliches Paar um den gesamten Ausdruck, auch wenn das vielleicht auf den ersten Blick so aussieht.

Man kann's auch so schreiben:

Code: Alles auswählen

dict(
    (k.strip(), v.strip()) for k,v in 
    (line.split(':', 1) for line in s.splitlines() if ':' in line)
)
(Nochmal optische Kosmetik gemacht)
Zuletzt geändert von snafu am Donnerstag 22. Oktober 2009, 18:58, insgesamt 2-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snafu hat geschrieben:Falls du mich meinst: Nö, das Entfernen des äußeren Klammernpaars erzeugt immer noch einen SyntaxError. Ist aber eigentlich auch richtig so. Man muss sich nur mal angucken, welches Klammernpaar jetzt wohin gehört. Da sind ist kein zusätzliches Paar um den gesamten Ausdruck, auch wenn das vielleicht auf den ersten Blick so aussieht.
Ja. Eeek, zu viele geschachtelte Generatoren. Ich sollte den Code erstmal testen bevor ich was sage :oops:
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten