Regular expression funktioniert so nicht

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.
Antworten
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Hallo zusammen,

ich möchte gerne die Inhalte zwischen den eckigen Klammern auslesen. Dazu habe ich folgendes geschrieben:

Code: Alles auswählen

import re

line = "error in date[02.02.2017] between line[487182, 487190]: invalid!"

bracket_content = re.findall(r"\[([a-zA-Z0-9_]+)\]", line)

print bracket_content
Die Ausgabe ist:
Was mache ich falsch bzw. übersehe ich?
Zuletzt geändert von Anonymous am Donnerstag 2. Februar 2017, 17:56, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
api
User
Beiträge: 181
Registriert: Donnerstag 7. August 2008, 21:23

Jetzt hab ich es selbst herausgefunden... (in grün)

So muss die regex-Zeile lauten: bracket_content = re.findall(r"\[([a-zA-Z0-9_., ]+)\]", line)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Code: Alles auswählen

re.findall(r'(?<=\[).*?(?=\])', line)
Das arbeitet mit Lookahead und Lookbehind. Ist daher leider auch entsprechend schlecht lesbar.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Das ist aber reichlich umständlich. Wenn du dir den String anschaust, also mit den eignen Augen, suchst du auch nach der öffnenden Klammern und schaust dann ob danach ein oder mehrere Buchstaben, Zahlen, Unterstriche, Punkte oder Kommas auftauchen, gefolgt von einer schliessenden Klammer? Mit deiner Anforderung stimmt dass zumindest nicht überein.

Wie wäre es einfach zu schauen ob eine Klammer auftaucht, gefolgt von irgendwas was keine schliessende Klammer ist, also dem Inhalt, gefolgt von der schliessenden Klammer?

Code: Alles auswählen

>>> import re
>>> line = "error in date[02.02.2017] between line[487182, 487190]: invalid!"
>>> re.findall(r'\[([^\]]+)\]', line)
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@DasIch: noch einfacher wird es durch die nicht-gefräßige Variante von `*`:

Code: Alles auswählen

re.findall(r'\[(.*?)\]', line)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Das ist kein regulärer Ausdruck mehr und benötigt Backtracking.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sirius3 hat geschrieben:@DasIch: noch einfacher wird es durch die nicht-gefräßige Variante von `*`:

Code: Alles auswählen

re.findall(r'\[(.*?)\]', line)
Das ist sogar noch besser als mein Lookbehind / Lookahead, da relativ gut lesbar. :)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

DasIch hat geschrieben:Das ist kein regulärer Ausdruck mehr und benötigt Backtracking.
Nur aus Interesse: Warum wird hier Backtracking benötigt? Ob ich ab dem Auftauchen eines [ alle Folgezeichen einsammle *solange kein* ] kommt (dein Ansatz) oder ob ich die Zeichen einsammle *bis* ein ] kommt (non-greedy Ansatz mit innerer Gruppe), ist das nicht letztlich genau das Gleiche?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Konnte es mir anhand der hier anführten Erklärungen selbst (hoffentlich korrekt) erklären: Eine Regex-Engine probiert stumpf schrittweise die einzelnen Bestandteile des regulären Ausdrucks durch. Das bedeutet, bei einem regulären Ausdruck abc (wobei a, b, und c für einen beliebigen Teilausdruck stehen) probiert die Engine zuerst, ob a matcht, bei Erfolg dann ob ab matcht und bei Erfolg dann ob abc matcht. Bei \[.*\] (habe non-greedy mal außen vor gelassen) würde die Engine also erst \[ (1) suchen, bei Erfolg dann \[.* (2) und bei Erfolg dann \[.*\] (3). Das wäre wegen Schritt 2 recht übel, was die Laufzeit angeht, weil dabei eben nicht gestoppt wird, wenn das ] auftaucht, da dieser dritte Schritt in dem Fall noch gar nicht beachtet wird. Vielmehr wird erstmal stumpf bis zum Ende probiert (.* = beliebige Anzahl beliebiger Zeichen) und sich danach per Backtracking "umständlich" der korrekten Zeichenkette angenähert. Beim Vorschlag von DasIch hingegen wird nicht über das Ziel hinausgeschossen, da [^\]]+ für alle Zeichen außer dem ] matcht und dies wegen dem + nur bis zum ersten Failure wiederholt wird. Danach geht er sofort zum nächsten Teilausdruck über. Ist das so richtig erklärt?
Antworten