Probleme mit regulären Ausdrücken

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
bremer
User
Beiträge: 109
Registriert: Sonntag 25. Mai 2008, 00:13

Code: Alles auswählen

>>> line = "40550 Cortex 0 -20 -282 1 0 27529 [] 21 1"
>>> import re
>>> p = re.compile("[a-z]+")
>>> p
<_sre.SRE_Pattern object at 0x013135A0>
>>> s = p.search(line)
>>> s
<_sre.SRE_Match object at 0x0137ED78>
>>> dir(s)
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
>>> s.string
'40550 Cortex 0 -20 -282 1 0 27529 [] 21 1'
>>> s.groups()
()
>>> s.span()
(7, 12)
>>> 
Ich verstehe den Sinn hinter dem Aufbau des re Moduls noch nicht.

1. Warum wird die zu durchsuchende Zeichenkette innerhalb einer Methode auf das Muster angewandt? Das ist zu Beginn extrem unintuitiv. Ich würde es jederzeit genau andersherum tun wollen.

2. Wenn eine Übereinstimmung gefunden wurde, befindet sich in s.string die gesamte vorherige Zeichenkette, in der die Übereinstimmung gefunden wurde. Welchen Sinn hat das denn? Ich will ja nur die jeweilige Übereinstimmung auslesen.

3. Die Methode search() stoppt nach dem ersten Fund. HALLO GEHT'S NOCH? Ich will natürlich alle Wörter eines bestimmten Musters herausfinden. Also man nimmt findall, split usw.

Nun habe ich folgendes Verhalten beobachtet:

Code: Alles auswählen

>>> re.split("\W+", line)
['40550', 'Cortex', '0', '20', '282', '1', '0', '27529', '21', '1']
>>> p = re.compile("\W")
>>> re.split(p, line)
['40550', 'Cortex', '0', '', '20', '', '282', '1', '0', '27529', '', '', '', '21', '1']
Zunächst muss man das Muster wohl doch nicht kompilieren. Aber es scheint einen Unterschied zu geben.

Das man die Methoden direkt vom re-Modul abrufen kann oder auf pattern-Objekte anwenden kann, verführt anfangs zusätzlich.

Was meinen Sie?
BlackJack

@bremer: 1. Was Du mit genau anders herum meinst, ist mir unverständlich. Der Sinn davon ein Pattern-Objekt zu erstellen liegt darin, dass eine Zeichenkette mit einem regulären Ausdruck ja nicht direkt zum Suchen verwendet werden kann. Daraus muss irgendwie ein Ablaufplan generiert werden, der beim abarbeiten dann tatsächlich eine Suche durchführt. Wenn man nach ein und dem selben Ausdruck mehrmals suchen will, bietet es sich infach an den regulären Ausdruck nur *einmal* in so ein Suchprogramm zu ersetzen um Zeit zu sparen.

2. Wenn Du die Zeichenkette in der gesucht wurde nicht benötigst, dann musst Du das `string`-Attribut auf dem Match-Objekt ja nicht beachten. Der Sinn ist, dass Du Treffer/Gruppen vom Match-Objekt abfragen kannst. Irgendwo müssen die Daten dafür herkommen. Nämlich von der ursprünglichen Zeichenkette. Auf die muss das Match-Objekt also Zugriff haben, also muss es eine Referenz darauf halten. Und die ist eben auch nach aussen sichtbar.

3. Code in der Standardbibliothek muss natürlich Deinen wünschen entsprechen. Hallo geht's noch!? Es gibt doch eine Methode die das macht was Du brauchst. Warum muss `search()` das machen was *Du* in *diesem* Fall lieber gehabt hättest?

4. Ja, wenn man zwei unterschiedliche reguläre Ausdrücke verwendet, kann es passieren, dass man auch unterschiedliche Ergebnisse bekommt. Damit konnte ja nun wirklich niemand rechnen… :roll:
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

bremer hat geschrieben:1. Warum wird die zu durchsuchende Zeichenkette innerhalb einer Methode auf das Muster angewandt? Das ist zu Beginn extrem unintuitiv. Ich würde es jederzeit genau andersherum tun wollen.
Nein, würdest du nicht. Der Grund für dieses Verhalten ist natürlich, dass man viel öfter mehrere Strings mittels desselben regulären Ausdrucks bearbeiten möchte, als einen einzelnen String mit mehreren regulären Ausdrücken.
bremer hat geschrieben:2. Wenn eine Übereinstimmung gefunden wurde, befindet sich in s.string die gesamte vorherige Zeichenkette, in der die Übereinstimmung gefunden wurde. Welchen Sinn hat das denn? Ich will ja nur die jeweilige Übereinstimmung auslesen.
In der Domukentation steht's doch:

Code: Alles auswählen

string
    The string passed to match() or search().
Also der String, auf den du die Regex-Suche anwendest, nicht das Ergebnis dieser Operation. Versuch mal:

Code: Alles auswählen

>>> s.group(0)
bremer hat geschrieben:3. Die Methode search() stoppt nach dem ersten Fund. HALLO GEHT'S NOCH? Ich will natürlich alle Wörter eines bestimmten Musters herausfinden. Also man nimmt findall, split usw.
Ich verstehe nicht, wo das Problem bei search() sein soll. Genauso könntest du dich darüber aufregen, dass man mit cd keine Dateien löschen kann oder mit wget keine Festplatte formatieren. Wenn es nicht die richtige Funktion für das ist, was du tun möchtest, dann nimm halt die, die dafür funktioniert.
bremer hat geschrieben:Zunächst muss man das Muster wohl doch nicht kompilieren. [...]Was meinen Sie?
Ich meine, dass ich gerne sowas mache:

Code: Alles auswählen

import re
tokenize = re.compile(flags=re.VERBOSE, pattern=r'''
    (?P<identifier> [a-zA-Z_]+\w* ) |
    (?P<number> \d+\.*\d* ) |
    (?P<add> \+ ) |
    (?P<sub> \- ) |
    (?P<mul> \* ) |
    (?P<div> \/ ) |
    (?P<assign> \= )
''').finditer

formula = 'x = a * 7.5 + 3 / b'

for token in tokenize(formula):
    print '{0:<12}{1}'.format(token.lastgroup, token.group(0))
Ergebnis:

Code: Alles auswählen

identifier  x
assign      =
identifier  a
mul         *
number      7.5
add         +
number      3
div         /
identifier  b
Dafür ist re.compile() sehr praktisch.

[EDIT]
Ich vergaß: "select isn't broken"
In specifications, Murphy's Law supersedes Ohm's.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

pillmuncher hat geschrieben: \d+\.*\d*
Beliebig viele Punkte? :) Du meinst bestimmt `\.?`. Oder `\d+(?:\.\d*)?`. Oder `(?:\d+(?:\.\d*)?|\.\d+)`, um auch die Ziffern vor dem Punkt optional zu machen. Reguläre Ausdrücke können selbst im X-Format schnell verwirren... Dennoch IMHO in Python der effizienteste Weg, Scanner zu bauen.

Stefan
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@sma: oops! :lol:

Vielleicht eher so:

Code: Alles auswählen

import re
tokenize = re.compile(flags=re.VERBOSE, pattern=r'''
    (?P<assignment> \= ) |
    (?P<identifier> [a-zA-Z]\w* ) |
    (?P<number> \d+\.?\d* | \.\d+ ) |
    (?P<add> \+ ) |
    (?P<sub> \- ) |
    (?P<mul> \* ) |
    (?P<div> \/ ) |
    (?P<left_paren> \( ) |
    (?P<right_paren> \) ) |
    (?P<end> $ ) |
    (?P<error> \S+)
''').finditer

formula = 'x = a * 7.5 + 3 / (b - (. + .4))'

for token in tokenize(formula):
    print '{0:12}{1}'.format(token.lastgroup, token.group(0))
Ergebnis:

Code: Alles auswählen

identifier  x
assignment  =
identifier  a
mul         *
number      7.5
add         +
number      3
div         /
left_paren  (
identifier  b
sub         -
left_paren  (
error       .
add         +
number      .4
right_paren )
right_paren )
end
Dass dabei auch zB. '2.' erkannt würde, ist Absicht.

Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.
Antworten