Seite 1 von 2

Hilfe zu regulären Ausdrücken / regex

Verfasst: Freitag 16. Oktober 2009, 19:43
von chrave
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

Verfasst: Freitag 16. Oktober 2009, 20:04
von DasIch
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.

Verfasst: Freitag 16. Oktober 2009, 21:32
von Defnull
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

Verfasst: Freitag 16. Oktober 2009, 21:56
von chrave
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.

Verfasst: Freitag 16. Oktober 2009, 22:00
von chrave
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.

:(

Verfasst: Freitag 16. Oktober 2009, 22:04
von Defnull
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.

Verfasst: Freitag 16. Oktober 2009, 22:26
von chrave
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.

Verfasst: Freitag 16. Oktober 2009, 22:30
von DasIch
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...

Verfasst: Freitag 16. Oktober 2009, 22:42
von chrave
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.

Verfasst: Samstag 17. Oktober 2009, 11:00
von jbs
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.

Verfasst: Samstag 17. Oktober 2009, 12:10
von sma
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

Verfasst: Donnerstag 22. Oktober 2009, 15:09
von chrave
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.

Verfasst: Donnerstag 22. Oktober 2009, 15:31
von snafu
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:

Verfasst: Donnerstag 22. Oktober 2009, 15:37
von derdon
Wozu die zusätzliche Klammerung im zweiten Argument von filter?

Verfasst: Donnerstag 22. Oktober 2009, 15:40
von snafu

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)

Verfasst: Donnerstag 22. Oktober 2009, 15:59
von derdon
Ah, da kriegt der Parser wohl Probleme.

Verfasst: Donnerstag 22. Oktober 2009, 16:20
von snafu
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'}

Verfasst: Donnerstag 22. Oktober 2009, 18:42
von Leonidas
Da das aber das einzige Argument ist, sollte ein äußeres Klammerpaar nun reichen.

Verfasst: Donnerstag 22. Oktober 2009, 18:45
von snafu
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)

Verfasst: Donnerstag 22. Oktober 2009, 18:49
von Leonidas
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: