Reguläre Ausdrücke: Validieren & splitten in einem Rutsch?

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
doppelkeks
User
Beiträge: 12
Registriert: Sonntag 14. Februar 2010, 22:28

Nichts wichtiges, nur aus Interesse...
ich hab es nicht hinbekommen, in einem Rutsch einen String sowohl zu validieren als auch in Häppchen zu zerhacken.

Wenn ich re.match nutze, dann finde ich in .groups() immer nur ein einziges Element (das zuletzt gefundene).
Wenn ich zB mit re.findAll rüberlaufe, dann merke ich nicht, wenn etwas verbotenes im string drin war.
Einfach nur aus Prinzip fuchst es mich, zweimal den ausdruck parsen zu müssen :)

Konkretes Beispiel:
Die Worte der zu validierenden Sprache bestehen aus einem 'n' oder 'd' gefolgt von einer Zahl. Es können beliebig viele Worte aneinander gereiht werden (zB: n1d2n5d9d7). Als Regex: '^([dn]\d)+$'
Jetzt möchte ich einerseits sicherstellen, dass ein Ausdruck nirgendwo die Grammatik verletzt, anderseits möchte ich die char-int Paare extrahieren.
Derzeit mach ich das in zwei Durchgängen:

Code: Alles auswählen

if not re.match(r'^([nd]\d)+$', expr):
    raise SomeError
for x in re.findall(r'[nd]\d', expr):
    print x
Ich schätze, ich steh einfach auf dem Schlauch, aber bin zu dickköpfig um es einfach gut sein zu lassen :roll:
deets

Ich denke nicht, dass das geht. Die Doku ist da eindeutig:

"""
Returns one or more subgroups of the match. If there is a single argument, the result is a single string;
"""

http://docs.python.org/library/re.html# ... ject.group

Es ist also immer nur der letzte Wert, der eine Gruppe gematcht hat. Damit bist du leider festgenagelt auf deine Loesung.
Benutzeravatar
DaMutz
User
Beiträge: 202
Registriert: Freitag 31. Oktober 2008, 17:25

du verwendest aber auch 2 Unterschiedliche reguläre Ausdrücke... Wenn du zweimal den selben Ausdruck hättest könntest du es so machen:

Code: Alles auswählen

import re

expr = 'n1d2n5d9d7'

pattern = re.compile(r'[nd]\d')

if not pattern.match(expr):
    raise SomeError
for x in pattern.findall(expr):
    print x
und falls der reguläre Ausdruck wirklich so einfach ist (und die Zahl immer aus einer Ziffer besteht), so könntest du im ersten Schritt validieren (wie bisher) und für das splitten bräuchtest du ja keinen regulären Ausdruck mehr, sondern könntest einfach mit jedem Schleifendurchgang 2 Zeichen verarbeiten.
deets

Das wuerde ihm aber auch nicht wirklich was bringen. Denn das was hier teuer ist (fuer welche Definition von "teuer" auch immer) ist ja genau die mehrfache iteration. Wenn die rexe das direkt aufsammeln wuerden, koennte man sich das sparen. Ob sich das Laufzeitverhalten nu aber gross aendert, durch dich impliziten appends, statt expliziter Verarbeitung - who knows. Der OP hat ja schon selbst gesagt, dass es eher ein "ich *will* aber!!!"-ding ist ;)
BlackJack

Wozu braucht man denn da unbedingt `re`?

Code: Alles auswählen

source = 'n1d2n5d9d7'
result = list()
for character, digit in (source[i:i+2] for i in xrange(0, len(source), 2)):
    if not (character in 'nd' and digit.isdigit()):
        raise Exception
    print character + digit
deets

@BlackJack

Weil's eine von wahrscheinlich 5 regulaeren Ausdrucks-basierten Loesungen ist, die tatsaechlich besser aussieht als deine :P
doppelkeks
User
Beiträge: 12
Registriert: Sonntag 14. Februar 2010, 22:28

Stimmt, ich "wollte es einfach so", und war angerissen, weil ichs nicht hinbekam

Hatte mich für re entschieden, weil
  • ich dachte, dass mein Parser mindestens fünfmal so lang geworden wäre wie BlackJacks... (elegant deine Lösung, gefällt mir)
  • ich vermutete, dass eine re Lösung in jedem Falle effizienter als eine python-Schleife sein wird.
Eigentlich ist die Geschwindigkeit hier aber wirklich egal, die Ausdrücke sind kurz, selbst 0.1 mal so schnell wäre sicherlich nicht spürbar. Mir gings einfach ums Prinzip, Eleganz, Spass und um Vermehrung von Wissen über das re Modul.

Ich hab einfach mal aus Interesse versucht, Deine Schleife so umzunudeln, dass eine "offene" for Schleife vermieden wird, aber mein Ergebnis ist nicht besser. Ich brauch zwei vollständige Durchläufe über den String und außerdem mehr Speicher :|
Aber weil ich jetzt ne Weile dran saß, (eigentlich wollt ich schon vor längst im Samstagabend_Partymodus sein...) poste ich es trotzdem :P

Code: Alles auswählen

s = 'n1d3n2n5'
result = [(c, int(d)) for c,d in zip(s[0::2], s[1::2]) if c in 'nd' and d.isdigit()]
if len(result)*2 != len(s):
    raise Exception
Antworten