Seite 1 von 3

Wiederholungen in Regex

Verfasst: Dienstag 14. Oktober 2008, 01:29
von snafu
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/

Verfasst: Dienstag 14. Oktober 2008, 06:34
von acoolon
Hau mich wenns falsch ist, aber war das nicht einfach *3

http://www.amk.ca/python/howto/regex/re ... 0000000000

acoolon

Verfasst: Dienstag 14. Oktober 2008, 07:39
von BlackJack
@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. ;-)

Verfasst: Dienstag 14. Oktober 2008, 10:18
von snafu
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.

Verfasst: Dienstag 14. Oktober 2008, 10:25
von fred.reichbier
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

Verfasst: Dienstag 14. Oktober 2008, 10:38
von snafu
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...

Verfasst: Dienstag 14. Oktober 2008, 11:27
von snafu
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?

Verfasst: Dienstag 14. Oktober 2008, 11:54
von BlackJack
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', '']

Verfasst: Dienstag 14. Oktober 2008, 12:04
von snafu
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.

Verfasst: Dienstag 14. Oktober 2008, 12:29
von BlackJack
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', '']

Verfasst: Dienstag 14. Oktober 2008, 13:28
von helduel
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

Verfasst: Dienstag 14. Oktober 2008, 13:56
von snafu
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/

Verfasst: Dienstag 14. Oktober 2008, 14:37
von BlackJack
@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'

Verfasst: Dienstag 14. Oktober 2008, 14:41
von snafu
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.

Verfasst: Dienstag 14. Oktober 2008, 15:05
von helduel
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

Verfasst: Dienstag 14. Oktober 2008, 15:08
von helduel
snafu hat geschrieben: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.
Folgendes sollte klappen:

Code: Alles auswählen

import socket
socket.gethostbyname(socket.gethostname())
# oder
socket.gethostbyname_ex(socket.gethostname())
Gruß,
Manuel

Verfasst: Dienstag 14. Oktober 2008, 15:12
von snafu
Das gibt den Localhost (127.0.1.1) zurück. Der interessiert mich ja nicht. ;)

Verfasst: Dienstag 14. Oktober 2008, 15:21
von helduel
Ok, dann schau mal hier nach. Da kannst du dir die IP via Interface-Namen herauskramen.

Verfasst: Dienstag 14. Oktober 2008, 16:55
von snafu
Und woher weiß ich was mein ifname ist?

Verfasst: Dienstag 14. Oktober 2008, 20:00
von roschi
hallo!

es geht zwar bestimmt noch einfacher, aber es gibt server, die dir via http deine ip zurueckgeben:

Code: Alles auswählen

import urllib
f = urllib.urlopen("http://checkip.dyndns.org/")
print f.read()
f.close()
mfg
roschi