Wiederholungen in 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.
Benutzeravatar
snafu
User
Beiträge: 5428
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wiederholungen in Regex

Beitragvon snafu » Dienstag 14. Oktober 2008, 01:29

Innerhalb eines Skripts zur Anzeige der IP habe ich folgenden Ausdruck:

Code: Alles auswählen

'\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}'


Ich habe mal gelesen, dass es eigentlich ausreicht, das Muster (also 1-3 stellige Zahl gefolgt von einem Punkt) einmal anzugeben und dann zu sagen wie oft es sich wiederholen soll (dreimal natürlich, denn das hinter der letzten Zahl kommt ja kein Punkt). Leider komme ich nicht darauf, wie ich das ausdrücken muss und wäre über Hilfe dankbar. :)

Sebastian

Das Skript: http://paste.pocoo.org/show/87905/
acoolon
User
Beiträge: 27
Registriert: Samstag 2. August 2008, 20:16

Beitragvon acoolon » Dienstag 14. Oktober 2008, 06:34

Hau mich wenns falsch ist, aber war das nicht einfach *3

http://www.amk.ca/python/howto/regex/regex.html#SECTION000320000000000000000

acoolon
BlackJack

Beitragvon BlackJack » Dienstag 14. Oktober 2008, 07:39

@snafu: Was hast Du denn schon versucht? Ich meine Du weisst ja ganz offensichtlich schon wie man eine Ziffer auf 1 bis 3 Wiederholungen begrenzt.

@acoolon: Fühl Dich gehauen. ;-)
Benutzeravatar
snafu
User
Beiträge: 5428
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Dienstag 14. Oktober 2008, 10:18

BlackJack hat geschrieben:Was hast Du denn schon versucht? Ich meine Du weisst ja ganz offensichtlich schon wie man eine Ziffer auf 1 bis 3 Wiederholungen begrenzt.


Naja, Klammern drum gesetzt:

Code: Alles auswählen

'(\d[.]){3}'


Aber da wird nichts gefunden. :(

Offen gesagt fehlt mir die Doku über eine Wiederholung von mehreren Zeichen. Ich denke, das Rumprobiere führt zu nichts.
fred.reichbier
User
Beiträge: 155
Registriert: Freitag 29. Dezember 2006, 18:27

Beitragvon fred.reichbier » Dienstag 14. Oktober 2008, 10:25

Hallo,

'(\d[.]){3}' passt z.B. nur auf 1.2.3. (beachte den Punkt am Ende ;)) - eine Zahl gefolgt von einem Punkt, und das dreimal.
Es geht bestimmt sauberer, aber z.B. r'(\d{1,3}(\.\d{1,3}){3})'' würde funktionieren:
eine bis drei Zahlen, gefolgt drei weiteren Zahlen, die jeweils hinter einem Punkt stehen ;)

Gruß Fred
Benutzeravatar
snafu
User
Beiträge: 5428
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Dienstag 14. Oktober 2008, 10:38

Die Lösung von Fred funktioniert:

Code: Alles auswählen

In [44]: s = 'bla 12.345.6.789 blupp'

In [45]: re.findall('(\d{1,3}([.]\d{1,3}){3})', s)[0][0]
Out[45]: '12.345.6.789'


Aber wirklich glücklich bin ich damit noch nicht...
Benutzeravatar
snafu
User
Beiträge: 5428
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Dienstag 14. Oktober 2008, 11:27

Wenn ich greedy bin, bekomme ich zumindest den ganzen Ip-String als eines der Elemente zurückgeliefert:

Code: Alles auswählen

In [64]: s
Out[64]: 'bla 12.34.567.890 blupp'

In [65]: re.findall('[\d+.]*', s)
Out[65]: ['', '', '', '', '12.34.567.890', '', '', '', '', '', '', '']


Kann ich direkt im Regex sagen, dass leere Ergebnisse nicht zurückgeliefert werden sollen?
Zuletzt geändert von snafu am Dienstag 14. Oktober 2008, 12:03, insgesamt 2-mal geändert.
BlackJack

Beitragvon BlackJack » Dienstag 14. Oktober 2008, 11:54

Schau mal was Du da noch alles findest:

Code: Alles auswählen

In [267]: re.findall(r'[\d+.]*', '1.2.3.4.5.6.7 . ... 42')
Out[267]: ['1.2.3.4.5.6.7', '', '.', '', '...', '', '42', '']
Benutzeravatar
snafu
User
Beiträge: 5428
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Dienstag 14. Oktober 2008, 12:04

Habe es noch etwas eingrenzen können:

Code: Alles auswählen

In [123]: re.findall('[\d+.]*\W', s)
Out[123]: ['1.2.3.4.5.6.7 ', '. ', '... ']


Weiter komm ich irgendwie nicht.
BlackJack

Beitragvon BlackJack » Dienstag 14. Oktober 2008, 12:29

Ich denke r'\d{1,3}(?:\.\d{1,3}){3}' ist noch mit das einfachste um vier dreistellige Zahlen, die durch Punkte getrennt sind zu matchen. Eine Funktion zum nachbearbeiten brauchst Du aber in jedem Fall, weil der Ausdruck auch auf 999.999.999.999 zutrifft.

Bei r'[\d+.]*' überleg doch mal *genau* was Du da beschreibst. Die vielen leeren Zeichenketten bekommst Du ganz einfach weg, in dem Du den Ausdruck so änderst, dass er eben nicht mehr auf die leere Zeichenkette passt, die ja sozusagen zwischen jedem Zeichenpaar in einer Zeichenkette steht.

Nachtrag: In Zeichenklassen gelten andere Regeln -- da ist ein '+' einfach nur ein '+':

Code: Alles auswählen

In [315]: re.findall('[\d+.]*', '1+2-3')
Out[315]: ['1+2', '', '3', '']
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Beitragvon helduel » Dienstag 14. Oktober 2008, 13:28

snafu hat geschrieben:Die Lösung von Fred funktioniert:

Code: Alles auswählen

In [44]: s = 'bla 12.345.6.789 blupp'

In [45]: re.findall('(\d{1,3}([.]\d{1,3}){3})', s)[0][0]
Out[45]: '12.345.6.789'


Aber wirklich glücklich bin ich damit noch nicht...


Warum? Weil das letzte .789 auch gefunden wird? Dann benutze einfach nicht-gruppierende Klammern:

Code: Alles auswählen

>>> s = 'bla 12.345.6.789 blupp'
>>> re.findall('(\d{1,3}(?:[.]\d{1,3}){3})', s)
['12.345.6.789']


Gruß,
Manuel
Benutzeravatar
snafu
User
Beiträge: 5428
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Dienstag 14. Oktober 2008, 13:56

Jupp, ich denke der letztgenannte Vorschlag ist der schönste. Danke an alle Beteiligten. :)

helduel hat geschrieben:Warum? Weil das letzte .789 auch gefunden wird? Dann benutze einfach nicht-gruppierende Klammern:


Ich fand die Gruppierung an sich unnötig, wusste aber nicht wie ich sie wegbekomme bzw dass sowas relativ einfach geht. Regexes sind nicht wirklich meine Stärke...

Aber mal eine generelle Frage: Die Variante "dreimal Digits-Punkt und einmal Digits" ist tätsächlich schwieriger darzustellen als "einmal Digits gefolgt von dreimal Punkt-Digits"?

EDIT: Hier mal der Code mit kleiner Fehlerbehandlung, sollte jetzt also halbwegs benutzbar sein (falls es jemanden interessiert) : http://paste.pocoo.org/show/87972/
BlackJack

Beitragvon BlackJack » Dienstag 14. Oktober 2008, 14:37

@snafu: Ich hoffe ich verderbe Dir jetzt nicht den ganzen Spass, aber das geht auch ohne externes `ping` und regulären Ausdruck:

Code: Alles auswählen

In [322]: socket.gethostbyname('www.heise.de')
Out[322]: '193.99.144.85'
Benutzeravatar
snafu
User
Beiträge: 5428
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Dienstag 14. Oktober 2008, 14:41

Absolut nicht. Mir war diese Möglichkeit nur nicht bekannt und ich dachte, wenn sich keiner meldet, gibt es da wohl nichts. ^^

Ist auch etwas internes für die eigene IP möglich? Ich wähle mich über ein DSL-Modem ein, falls das von Belang ist.
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Beitragvon helduel » Dienstag 14. Oktober 2008, 15:05

snafu hat geschrieben:Aber mal eine generelle Frage: Die Variante "dreimal Digits-Punkt und einmal Digits" ist tätsächlich schwieriger darzustellen als "einmal Digits gefolgt von dreimal Punkt-Digits"?


Nö, nicht die Bohne:

Code: Alles auswählen

# Digits + 3x Punkt-Digits
re.findall('(\d{1,3}(?:[.]\d{1,3}){3})', s)
# 3x Digits-Punkt + Digits
re.findall('((?:\d{1,3}[.]){3}\d{1,3})', s)


Ist ja nur rumgedreht, aber sonst das gleiche.

Gruß,
Manuel

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder