Probleme mit 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.
Antworten
HPL
User
Beiträge: 10
Registriert: Mittwoch 1. Dezember 2010, 14:29

Code: Alles auswählen

p1 = re.compile('@relationS\*\s*@data', re.IGNORECASE)
print re.match(p1, text).group() 
Habe p1 als Regex.

text ist ein String, der mit "@relation" beginnt und mit "@data" enthält, zwischen @relation und @data kommen alle möglichen Buchstaben vor, auch Leerzeichen und " ".

Ich will nun den Text von Relation bis @data print re.match(p1, text).group() ausdrucken, group() soll ja angeblich einen String hergeben.

Fehler lautet:

print re.match(p1, text).group()
Attribute Error: 'NoneType' object has no attribute 'group'

Aus der Doku werd ich nicht schlau. Hat jemand ne ID? Hab ich die Regex zu blöd geschrieben?

mit \S*\s* wollte ich sagen: entweder ein non-whitespace oder ein whitespace kommt jeweils(!) beliebig oft.

Ich habe es auch schon mit der Wildcard .* versucht (auch so: \,*). Is nix zu wollen. :evil:
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

Deine Regex ist kaputt. Deine jetzige sucht nach „@relationS*“ gefolgt von beliebig viel Whitespace und „@data“.
Was du willst ist:

Code: Alles auswählen

In [2]: DATA = '@relation spam spam spam @data'

In [3]: pattern = re.compile('@relation(.*)@data')

In [4]: pattern.findall(DATA)
Out[4]: [' spam spam spam ']
In meiner Regex mache ich Gebrauch von „Gruppen“. So fasse ich alle Zeichen zwischen den Klammern zusammen, sodass ich diese als Ergebnis von `pattern.findall(...)` erhalte.
Reguläre Ausdrücke sollte man in Python übrigens als Rawstrings schreiben. Dann werden Backslashes anders behandelt.

Code: Alles auswählen

In [5]: '\b'
Out[5]: '\x08'

In [6]: r'\b'
Out[6]: '\\b'
Zuletzt geändert von nomnom am Montag 27. Februar 2012, 22:10, insgesamt 3-mal geändert.
BlackJack

@HPL: Die `match()`-Funktion gibt `None` zurück wenn das Muster nicht auf den Text passt, und das `None`-Objekt hat keine `group()`-Methode. Deshalb die Ausnahme.

Im Quelltext steht 'S\*\s*', im Text dagegen '\S*\s*'!? Gib doch mal ein komplettes Beispiel an. Also auch einen oder mehrere Beispieltexte auf welche die `match()`-Funktion angewendet werden soll.

'\S*\s*' bedeutet übrigens 0 oder mehr nicht-whitespace *gefolgt* von 0 oder mehr whitespace Zeichen und nicht das eine *oder* das andere. Wenn das eigentlich nicht-whitespace oder whitespace und davon beliebig viele (inklusive keine) bedeuten sollte, dann wäre ein Wildcard '.*' tatsächlich die kürzere und einfacherere Art das auszudrücken.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Damit "." auch auf Zeilenumbrüche reagiert (was "\s" machen würde) muss noch re.DOTALL gesetzt werden.

Stefan
HPL
User
Beiträge: 10
Registriert: Mittwoch 1. Dezember 2010, 14:29

Und wenn er nun beliebig viele beliebige Zeichen, die von einem Komma gefolgt werden, 4 mal matchen soll, also z. B. sdfsdf,43543gfg, 333, hjkö,

Kann ich das dann als [(*),]{4} ?

Dieser Versuch war bei mir näml. von keinem großen Erfolg gekrönt :K
BlackJack

@HPL: Wie kommst Du denn auf eckige Klammern dafür? Und worauf bezieht sich der '*' in dem Ausdruck? Du musst schon die Dokumentation lesen und verstehen und nicht einfach wild irgend etwas ausprobieren was so ähnlich wie ein regulärer Ausdruck aus sieht.
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Code: Alles auswählen

re.match("((.*,){4})", "123, 345, abc, xyz,").group()
Ich wette der folgende Verbesserungsvorschlag wäre eines Code-Golf-Wettbewerbs würdig.

edit: Wobei man auch einfach den fraglichen String auf die Anzahl der Kommata prüfen könnte.
BlackJack

@webspider: Die äusseren Klammern definieren eine unnötige Gruppe, weil der komplette Treffer immer Gruppe 0 ist. Die man auch bekommt wenn man die `group()`-Methode ohne Argument aufruft.

Dafür sollte man noch ein '?' verwenden um '*' „non-greedy” zu machen, und damit unnötiges „backtracking” bei der Suche vermeiden.
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Ach so ist das. Du meinst also, dass bevor der Treffer "123, 345, abc, xyz," (der das erste Ergebnis einer gierigen Suche wäre) verworfen wird, dieser nochmals vom Inhalt her untersucht wird auf passende Teilstrings (was komplexer wäre als auf die ungierige Weise direkt nach den kleinstmöglichen Treffern zu suchen). Gut, das mit den Klammern war schon offensichtlicher gewesen :mrgreen:

Code: Alles auswählen

re.match("(.*?,){4}", "123, 345, abc, xyz,").group()
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Will man das letzte Komma wirklich haben? Falls nein, würde ich ".*?(?:,.*?){3}" als effiziente Variante empfehlen.

Stefan
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

sma hat geschrieben:Will man das letzte Komma wirklich haben? Falls nein, würde ich ".*?(?:,.*?){3}" als effiziente Variante empfehlen.
Ich würde gar keinen regulären Ausdrück dafür empfehlen. Eher das:

Code: Alles auswählen

','.join(DATA.split(',', 4)[:-1])
Es ist kürzer und schneller … (Okay, drei Mikrosekunden Unterschied ist wohl nicht so enorm …)
Antworten