Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by haas.homelinux.net...

Inhaltsverzeichnis
1 Einleitung
2 Überblick über Python
3 Die Arbeit mit Python
4 Der interaktive Modus
5 Grundlegendes zu Python-Programmen
6 Kontrollstrukturen
7 Das Laufzeitmodell
8 Basisdatentypen
9 Benutzerinteraktion und Dateizugriff
10 Funktionen
11 Modularisierung
12 Objektorientierung
13 Weitere Spracheigenschaften
14 Mathematik
15 Strings
16 Datum und Zeit
17 Schnittstelle zum Betriebssystem
18 Parallele Programmierung
19 Datenspeicherung
20 Netzwerkkommunikation
21 Debugging
22 Distribution von Python-Projekten
23 Optimierung
24 Grafische Benutzeroberflächen
25 Python als serverseitige Programmiersprache im WWW mit Django
26 Anbindung an andere Programmiersprachen
27 Insiderwissen
28 Zukunft von Python
A Anhang
Stichwort

Download:
- ZIP, ca. 4,8 MB
Buch bestellen
Ihre Meinung?

Spacer
 <<   zurück
Python von Peter Kaiser, Johannes Ernesti
Das umfassende Handbuch - Aktuell zu Python 2.5
Buch: Python

Python
gebunden, mit CD
819 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1110-9
Pfeil 15 Strings
  Pfeil 15.1 Arbeiten mit Zeichenketten – string
    Pfeil 15.1.1 Ein einfaches Template-System
  Pfeil 15.2 Reguläre Ausdrücke – re
    Pfeil 15.2.1 Syntax regulärer Ausdrücke
    Pfeil 15.2.2 Verwendung des Moduls
    Pfeil 15.2.3 Ein einfaches Beispielprogramm – Searching
    Pfeil 15.2.4 Ein komplexeres Beispielprogramm – Matching
  Pfeil 15.3 Lokalisierung von Programmen – gettext
    Pfeil 15.3.1 Beispiel für die Verwendung von gettext
  Pfeil 15.4 Hash-Funktionen – hashlib
    Pfeil 15.4.1 Verwendung des Moduls
    Pfeil 15.4.2 Beispiel
  Pfeil 15.5 Dateiinterface für Strings – StringIO


Galileo Computing - Zum Seitenanfang

15.2 Reguläre Ausdrücke – re  Zur nächsten ÜberschriftZur vorigen Überschrift

Das Modul re der Standardbibliothek bietet umfangreiche Möglichkeiten zum Arbeiten mit sogenannten regulären Ausdrücken (engl. regular expressions). In einem solchen regulären Ausdruck wird durch eine spezielle Syntax ein Textmuster beschrieben, das dann auf verschiedene Texte oder Textfragmente angewendet werden kann. Grundsätzlich gibt es zwei große Anwendungsbereiche von regulären Ausdrücken.

Im ersten Bereich, beim sogenannten Matching, wird geprüft, ob ein Textabschnitt auf das Muster des regulären Ausdrucks passt oder nicht. Ein häufiges Beispiel für Matching wäre es zu testen, ob eine eingegebene E-Mail-Adresse syntaktisch gültig ist.

Die zweite Einsatzmöglichkeit von regulären Ausdrücken ist das sogenannte Searching, bei dem innerhalb eines größeren Textes nach Textfragmenten gesucht wird, die auf einen regulären Ausdruck passen. Es handelt sich dabei um eine eigene Disziplin, da dieses Verhalten vom Programmierer selbst nicht effizient durch Einsatz des Matchings implementiert werden kann. Ein Anwendungsbeispiel könnte der Syntax Highlighter Ihrer Python-Umgebung sein, der durch Searching nach speziellen Codeabschnitten wie Schlüsselwörtern oder Strings sucht, um diese grafisch hervorzuheben.

Ein regulärer Ausdruck ist in Python ein String, der die entsprechenden Regeln enthält. Im Gegensatz zu manch anderen Programmiersprachen existiert hier kein eigenes Literal zu diesem Zweck.

Sollten Sie sich mit regulären Ausdrücken bereits auskennen, sind Sie vielleicht gerade auf ein Problem aufmerksam geworden, denn der Backslash ist ein sehr wichtiges Zeichen zur Beschreibung regulärer Ausdrücke, und ausgerechnet dieses Zeichen trägt innerhalb eines Strings bereits Bedeutung. Normalerweise leitet ein Backslash eine Escape-Sequenz ein. Sie können nun entweder immer die Escape-Sequenz für einen Backslash ("\\") verwenden oder, was empfehlenswerter ist, auf Pythons Raw-Strings zurückgreifen, in denen keine Escape-Sequenzen möglich sind. Zur Erinnerung: Raw-Strings werden in Python durch ein vorangestelltes r gekennzeichnet:

r"\Hallo Welt"

Im Folgenden möchten wir Sie in die komplexe Syntax regulärer Ausdrücke einweihen. Allein zu diesem Thema sind bereits ganze Bücher erschienen, weswegen die Beschreibung hier vergleichsweise knapp, aber grundlegend ausfallen soll.

Es gibt verschiedene Notationen zur Beschreibung regulärer Ausdrücke. Python hält sich an die Syntax, die in der Programmiersprache Perl verwendet wird.


Galileo Computing - Zum Seitenanfang

15.2.1 Syntax regulärer Ausdrücke  Zur nächsten ÜberschriftZur vorigen Überschrift

Grundsätzlich ist der String

r"python"

bereits ein regulärer Ausdruck. Dieser würde exakt auf den String "python" passen. Diese direkt angegebenen einzelnen Buchstaben werden Zeichenliterale genannt. Beachten Sie unbedingt, dass Zeichenliterale innerhalb regulärer Ausdrücke case sensitive sind, das heißt, dass der obige Ausdruck nicht auf den String "Python" passen würde.

In regulären Ausdrücken können eine ganze Reihe von Steuerungszeichen verwendet werden, die den Ausdruck flexibler und mächtiger machen. Diese sollen im Folgenden besprochen werden.

Beliebige Zeichen

Die einfachste Verallgemeinerung, die innerhalb eines regulären Ausdrucks verwendet werden kann, ist die Kennzeichnung eines beliebigen Zeichens durch einen Punkt. So passt der Ausdruck

r".ython"

sowohl auf "python", "Python" als auch auf "Jython", nicht jedoch auf "Blython", da es sich nur um ein einzelnes beliebiges Zeichen handelt. Ein durch einen Punkt gekennzeichnetes beliebiges Zeichen darf nicht weggelassen werden. Der obige Ausdruck würde demzufolge nicht auf "ython" passen.

Zeichenklassen

Abgesehen davon, ein Zeichen ausdrücklich als beliebig zu kennzeichnen, ist es auch möglich, eine Klasse von Zeichen vorzugeben, die an dieser Stelle vorkommen dürfen. Dazu werden die gültigen Zeichen in eckige Klammern an die entsprechende Position geschrieben:

r"[jp]ython"

Dieser reguläre Ausdruck arbeitet ähnlich wie der des letzten Abschnitts, lässt jedoch nur die Buchstaben j und p als erstes Zeichen des Wortes zu. Damit passt der Ausdruck sowohl auf "jython" als auch auf "python". Der Ausdruck passt aber nicht auf "Python", "jpython" oder "ython". Um auch die jeweiligen Großbuchstaben im Wort zu erlauben, kann der Ausdruck folgendermaßen erweitert werden:

r"[jJpP]ython"

Innerhalb einer Zeichenklasse ist es ebenfalls möglich, ganze Bereiche von Zeichen zuzulassen. Dadurch wird folgende Syntax verwendet:

r"[A-Z]ython"

Dieser reguläre Ausdruck lässt jeden Großbuchstaben als Anfangsbuchstaben des Wortes durch, beispielsweise aber keinen Kleinbuchstaben und keine Zahl. Um mehrere Bereiche zuzulassen, werden diese ganz einfach hintereinander geschrieben:

r"[A-Ra-r]ython"

Dieser reguläre Ausdruck passt beispielsweise sowohl auf "Qython" als auch auf "qython", nicht aber auf "Sython" oder "3ython".

Auch Ziffernbereiche können als Zeichenklasse verwendet werden:

r"[0-9]ython"

Als letzte Möglichkeit, die eine Zeichengruppe bietet, können Zeichen oder Zeichenbereiche ausgeschlossen werden. Dazu wird zu Beginn der Zeichengruppe ein Zirkumflex (^) geschrieben. So erlaubt der reguläre Ausdruck

r"[^pP]ython"

jedes Zeichen, abgesehen von einem großen oder kleinen »P«. Demzufolge würden sowohl "Sython" als auch "wython" passen, während "Python" und "python" außen vor bleiben müssten.

Beachten Sie, dass es innerhalb einer Zeichenklasse, abgesehen vom Bindestrich und dem Zirkumflex, keine Zeichen mit spezieller Bedeutung gibt. Das heißt insbesondere, dass ein Punkt in einer Zeichenklasse tatsächlich das Zeichen . bedeutet und nicht etwa ein beliebiges Zeichen.

Quantoren

Bisher können wir in einem regulären Ausdruck bestimmte Regeln für einzelne Zeichen aufstellen. Wir würden allerdings vor einem Problem stehen, wenn wir an einer bestimmten Stelle des Wortes eine gewisse Anzahl oder gar beliebig viele dieser Zeichen erlauben wollten. Für diesen Zweck werden sogenannte Quantoren eingesetzt. Das sind spezielle Zeichen, die hinter ein einzelnes Zeichenliteral oder eine Zeichenklasse geschrieben werden und kennzeichnen, wie oft diese auftreten dürfen. Die folgende Tabelle listet alle Quantoren auf und erläutert kurz ihre Bedeutung. Danach werden wir Beispiele für die Verwendung von Quantoren bringen.


Tabelle 15.2  Quantoren in regulären Ausdrücken
Quantor Bedeutung

?

Das vorangegangene Zeichen bzw. die vorangegangene Zeichenklasse darf entweder keinmal oder einmal vorkommen.

*

Das vorangegangene Zeichen bzw. die vorangegangene Zeichenklasse darf beliebig oft hintereinander vorkommen, das heißt unter anderem, dass sie auch weggelassen werden kann.

+

Das vorangegangene Zeichen bzw. die vorangegangene Zeichenklasse darf beliebig oft hintereinander vorkommen, mindestens aber einmal. Sie darf also nicht weggelassen werden.


Die folgenden drei Beispiele zeigen einen regulären Ausdruck mit je einem Quantor. Nachfolgend soll besprochen werden, wie sich diese auf die Bedeutung des Ausdrucks auswirken.

  • r"P[Yy]?thon"
    • Dieser reguläre Ausdruck erwartet an der zweiten Stelle des Wortes ein höchstens einmaliges Auftreten des großen oder kleinen »Y«. Damit passt der Ausdruck auf die Wörter "Python" und "Pthon", beispielsweise jedoch nicht auf "Pyython".
  • r"P[Yy]*thon"
    • Dieser reguläre Ausdruck erwartet an der zweiten Stelle des Wortes ein beliebig häufiges Auftreten des großen oder kleinen »Y«. Damit passt der Ausdruck auf die Wörter "Python", "Pthon" und "PyyYYYyython", beispielsweise jedoch nicht auf "Pzthon".
  • r"P[Yy]+thon"
    • Dieser reguläre Ausdruck erwartet an der zweiten Stelle des Wortes ein mindestens einmaliges Auftreten des großen oder kleinen »Y«. Damit passt der Ausdruck auf die Wörter "Python", "PYthon" und "PyyYYYyython", beispielsweise jedoch nicht auf "Pthon".

Neben diesen allgemeinen Quantoren gibt es eine Syntax, die es ermöglicht, exakt anzugeben, wie viele Wiederholungen einer Zeichengruppe erlaubt sind. Dabei werden die Unter- und Obergrenzen für Wiederholungen in geschweifte Klammern hinter das entsprechende Zeichen bzw. die entsprechende Zeichengruppe geschrieben. Die folgende Tabelle listet die Möglichkeiten der Notation auf:


Tabelle 15.3  Quantoren in regulären Ausdrücken
Quantor Bedeutung

{anz}

Das vorangegangene Zeichen bzw. die vorangegangene Zeichenklasse muss exakt anz-mal vorkommen.

{min,}

Das vorangegangene Zeichen bzw. die vorangegangene Zeichenklasse muss mindestens min-mal vorkommen.

{,max}

Das vorangegangene Zeichen bzw. die vorangegangene Zeichenklasse darf maximal max-mal vorkommen.

{min,max}

Das vorangegangene Zeichen bzw. die vorangegangene Zeichenklasse muss mindestens min-mal und darf maximal max-mal vorkommen.


Auch für diese Quantoren möchten wir das bisherige Beispiel abändern und untersuchen, was sie für Auswirkungen haben.

  • r"P[Yy]{2}thon"
    • Dieser reguläre Ausdruck erwartet an der zweiten Stelle des Wortes exakt zwei jeweils große oder kleine »Y«. Damit passt der Ausdruck auf die Wörter "Pyython" oder "PYython", beispielsweise jedoch nicht auf "Pyyython".
  • r"P[Yy]{2,}thon"
    • Dieser reguläre Ausdruck erwartet an der zweiten Stelle des Wortes mindestens zwei jeweils große oder kleine »Y«. Damit passt der Ausdruck auf die Wörter "Pyython", "PYython" und "PyyYYYyython", beispielsweise jedoch nicht auf "Python".
  • r"P[Yy]{,2}thon"
    • Dieser reguläre Ausdruck erwartet an der zweiten Stelle des Wortes maximal zwei jeweils große oder kleine »Y«. Damit passt der Ausdruck auf die Wörter "Python", "Pthon" und "PYYthon", beispielsweise jedoch nicht auf "Pyyython".
  • r"P[Yy]{1,2}thon"
    • Dieser reguläre Ausdruck erwartet an der zweiten Stelle des Wortes mindestens ein und maximal zwei große oder kleine »Y«. Damit passt der Ausdruck auf die Wörter "Python" oder "PYython", beispielsweise jedoch nicht auf "Pthon" oder "PYYYthon".

Vordefinierte Zeichenklassen

Damit nicht bei jedem regulären Ausdruck das Rad neu erfunden werden muss, existiert eine Reihe von vordefinierten Zeichenklassen, die beispielsweise alle Ziffern oder alle alphanumerischen Zeichen umfassen. Diese Zeichenklassen werden bei der Arbeit mit regulären Ausdrücken sehr häufig benötigt und können deswegen durch einen speziellen Code abgekürzt werden. Jeder dieser Codes beginnt mit einem Backslash. Die folgende Tabelle listet alle vordefinierten Zeichenklassen mit ihren Bedeutungen auf.


Tabelle 15.4  Vordefinierte Zeichenklassen in regulären Ausdrücken
Zeichenklasse Bedeutung

\d

Passt auf alle Zeichen, die Ziffern des Dezimalsystems sind. Äquivalent zu [0-9].

\D

Passt auf alle Zeichen, die nicht Ziffern des Dezimalsystems sind. Äquivalent zu [^0-9].

\s

Passt auf alle Whitespace-Zeichen. Äquivalent zu [ \t\n\r\f\v].

\S

Passt auf alle Zeichen, die kein Whitespace sind. Äquivalent zu [^ \t\n\r\f\v].

\w

Passt auf alle alphanumerischen Zeichen und den Unterstrich. Äquivalent zu [a-zA-z0-9_].

\W

Passt auf alle Zeichen, die nicht alphanumerisch und kein Unterstrich sind. Äquivalent zu [^a-zA-Z0-9_].


Diese vordefinierten Zeichenklassen können wie ein normales Zeichen im regulären Ausdruck verwendet werden. So passt der Ausdruck

r"P\w*th\dn"

auf die Wörter "Pyth0n" oder "P_th1n", beispielsweise jedoch nicht auf "Python".

Beachten Sie, dass die üblichen Escape-Sequenzen, die innerhalb eines Strings verwendet werden können, auch innerhalb eines regulären Ausdrucks – selbst wenn er in einem Raw-String geschrieben wird – ihre Bedeutung behalten und nicht mit den hier vorgestellten Zeichenklassen interferieren. Gebräuchlich sind hier vor allem \n, \t, \r oder \\, insbesondere aber auch \x.

Zudem ist es mit dem Backslash möglich, einem Sonderzeichen die spezielle Bedeutung zu nehmen, die es innerhalb eines regulären Ausdrucks trägt. Auf diese Weise kann zum Beispiel mit den Zeichen * oder + gearbeitet werden, ohne dass diese als Quantoren angesehen werden. So passt der folgende reguläre Ausdruck

r"\*Py\.\.\.on\*"

allein auf den String "*Py...on*".

Weitere Sonderzeichen

Für gewisse Einsatzgebiete wird es unbedingt verlangt, Regeln aufstellen zu können, die über die bloße Zeichenebene hinausgehen. So wäre es beispielsweise interessant, einen regulären Ausdruck zu erschaffen, der nur passt, wenn sich das Wort am Ende oder Anfang einer Textzeile befindet. Für solche und ähnliche Fälle gibt es einen bestimmten Satz an zusätzlichen Sonderzeichen, die genau so angewendet werden können wie die vordefinierten Zeichenklassen.

Die folgende Tabelle listet alle zusätzlichen Sonderzeichen auf und gibt zu jedem eine kurze Erklärung. In der Tabelle finden Sie einige Anmerkungen zu sogenannten Flags. Das sind Einstellungen, die entweder aktiviert oder deaktiviert werden können und die Auswertung eines regulären Ausdrucks beeinflussen. Näheres dazu, wie Sie diese Einstellungen setzen können, erfahren Sie im Laufe dieses Kapitels.


Tabelle 15.5  Vordefinierte Zeichenklassen in regulären Ausdrücken
Sonderzeichen Bedeutung

\A

Passt nur am Anfang eines Strings.

\b

Passt nur am Anfang oder Ende eines Wortes. Ein Wort kann aus allen Zeichen der Klasse \w bestehen und wird durch ein Zeichen der Klasse \s begrenzt.

\B

Passt nur, wenn es sich nicht um den Anfang oder das Ende eines Wortes handelt.

\Z

Passt nur am Ende eines Strings.

^

Passt nur am Anfang eines Strings.

Beachten Sie, dass das Zeichen ^ zwei Bedeutungen hat und innerhalb einer Zeichenklasse die aufgelisteten Zeichen ausschließt.

Wenn das MULTILINE-Flag gesetzt wurde, passt ^ auch direkt nach jedem Newline-Zeichen innerhalb des Strings.

$

Passt nur am Ende eines Strings.

Wenn das MULTILINE-Flag gesetzt wurde, passt $ auch direkt vor jedem Newline-Zeichen innerhalb des Strings.


Im konkreten Beispiel passt also der reguläre Ausdruck

r"\APython\Z"

nur bei dem String "Python", nicht jedoch bei den Strings "abcPythonabc" oder "Pythonabc".

Die hier besprochenen Beispiele beziehen sich hauptsächlich auf das Matching von regulären Ausdrücken, weswegen Ihnen die Bedeutung dieser Sonderzeichen möglicherweise noch nicht ersichtlich ist. Diese Sonderzeichen sind aber gerade beim Searching von unerlässlicher Wichtigkeit. Stellen Sie sich einmal vor, Sie würden in einem Text nach allen Vorkommen einer bestimmten Zeichenkette am Zeilenanfang suchen wollen. Dies wäre nur durch Einsatz des Sonderzeichens ^ möglich.

Genügsame Quantoren

Wir haben bereits die Quantoren ?, * und + besprochen. Diese werden in der Terminologie regulärer Ausdrücke als »gefräßig« (engl. greedy) bezeichnet. Diese Klassifizierung ist nur beim Searching von Bedeutung. Betrachten Sie dazu einmal folgenden regulären Ausdruck:

r"Py.*on"

Dieser Ausdruck passt auf jeden Teilstring, der mit Py beginnt und mit on endet. Dazwischen können beliebig viele, nicht näher spezifizierte Zeichen stehen. Behalten Sie im Hinterkopf, dass wir uns beim Searching befinden, der Ausdruck also dazu verwendet werden soll, aus einem längeren String verschiedene Teilstrings zu isolieren, die auf den regulären Ausdruck passen.

Nun möchten wir den regulären Ausdruck gedanklich auf den folgenden String anwenden:

"Python Python Python"

Sie meinen, dass drei Ergebnisse gefunden werden? Irrtum, es handelt sich um exakt ein Ergebnis, nämlich den Teilstring "Python Python Python". Zur Erklärung: Es wurde der »gefräßige« Quantor * eingesetzt. Ein solcher gefräßiger Quantor hat die Ambition, die maximal mögliche Anzahl Zeichen zu »verschlingen«. Beim Searching wird also, solange die »gefräßigen« Quantoren eingesetzt werden, stets der größtmögliche passende String gefunden.

Dieses Verhalten kann umgekehrt werden, sodass immer der kleinstmögliche passende String gefunden wird. Dazu kann an jeden Quantor ein Fragezeichen angefügt werden. Dadurch wird der Quantor »genügsam« (engl. non-greedy). Angenommen, das Searching auf dem obigen String wäre mit dem regulären Ausdruck

r"Py.*?on"

durchgeführt worden, so wäre als Ergebnis tatsächlich dreimal der Teilstring "Python" gefunden worden. Dies funktioniert für die Quantoren ?, *, + und {}.

Gruppen

Ein Teil eines regulären Ausdrucks kann durch runde Klammern zu einer sogenannten Gruppe zusammengefasst werden. Eine solche Gruppierung hat im Wesentlichen drei Vorteile:

  • Eine Gruppe kann als Einheit betrachtet und als solche natürlich auch mit einem Quantor versehen werden. Auf diese Weise lässt sich beispielsweise das mehrmalige Auftreten einer bestimmten Zeichenkette erlauben:
r"( ?Python)+ ist gut"
    • In diesem Ausdruck existiert eine Gruppe um den Teilausdruck r" ?Python". Dieser Teilausdruck passt auf den String "Python" mit einem optionalen Leerzeichen zu Beginn. Die gesamte Gruppe kann nun beliebig oft vorkommen, womit der obige reguläre Ausdruck sowohl auf "Python ist gut" als auch auf "Python Python Python ist gut" passt.
    • Beachten Sie das Leerzeichen zu Beginn der Gruppe, um die Funktionsweise des Ausdrucks zu verstehen.
  • Der zweite Vorteil einer Gruppe ist der, dass man auf sie zugreifen kann, nachdem das Searching bzw. Matching durchgeführt wurde. Das heißt, man könnte beispielsweise überprüfen, ob eine eingegebene URL gültig ist, und gleichzeitig Subdomain, Domain und TLD herausfiltern.
  • Es gibt Gruppen, die in einem regulären Ausdruck häufiger gebraucht werden. Um diese nicht jedes Mal erneut schreiben zu müssen, werden Gruppen mit 1 beginnend durchnummeriert und können dann anhand ihres Index referenziert werden. Eine solche Referenz besteht aus einem Backslash, gefolgt von dem Index der jeweiligen Gruppe, und passt auf den gleichen Teilstring, auf den die Gruppe gepasst hat. So passt der reguläre Ausdruck
r"(Python) \1"
    • auf "Python Python".

Alternativen

Eine weitere Möglichkeit, die die Syntax regulärer Ausdrücke vorsieht, sind sogenannte Alternativen. Im Prinzip handelt es sich dabei um nichts anderes als um eine ODER-Verknüpfung zweier Zeichen oder Zeichengruppen, wie Sie sie bereits von dem Operator or her kennen. Diese Verknüpfung wird durch den senkrechten Strich, auch Pipe genannt, durchgeführt.

r"P(ython|eter)"

Dieser reguläre Ausdruck passt sowohl auf den String "Python" als auch auf "Peter". Durch die Gruppe kann später ausgelesen werden, welche der beiden Alternativen aufgetreten ist.

Extensions

Damit wäre die Syntax regulärer Ausdrücke beschrieben. Zusätzlich zu dieser mehr oder weniger standardisierten Syntax erlaubt Python die Verwendung sogenannter Extensions. Eine Extension ist folgendermaßen aufgebaut:

(?...)

Die drei Punkte werden durch eine Kennung der gewünschten Extension und weitere extension-spezifische Angaben ersetzt. Diese Syntax wurde gewählt, da eine öffnende Klammer, gefolgt von einem Fragezeichen, keine syntaktisch sinnvolle Bedeutung hat und demzufolge »frei« war. Beachten Sie aber, dass eine Extension in der Regel keine neue Gruppe erzeugt, auch wenn die runden Klammern dies nahelegen. Nachfolgend möchten wir näher auf die Extensions eingehen, die in Pythons regulären Ausdrücken verwendet werden können.

(?iLmsux)

Diese Extension erlaubt es, ein oder mehrere Flags für den gesamten regulären Ausdruck zu setzen. Der Begriff Flag ist bereits verwendet worden und beschreibt eine bestimmte Einstellung, die entweder aktiviert oder deaktiviert werden kann. Ein Flag kann entweder im regulären Ausdruck selbst, eben durch diese Extension, oder durch einen Parameter der Funktion re.compile gesetzt werden. Im Zusammenhang mit dieser Funktion werden wir näher darauf eingehen, welche Flags wofür stehen. Das Flag i macht den regulären Ausdruck beispielsweise case insensitive:

r"(?i)P"

Dieser Ausdruck passt sowohl auf "P" als auch auf "p".

(?:…)

Diese Extension kann wie normale runde Klammern verwendet werden, erzeugt dabei aber keine Gruppe. Das heißt, auf einen durch diese Extension eingeklammerten Teilausdruck kann später nicht zugegriffen werden. Ansonsten ist diese Syntax äquivalent zu runden Klammern:

r"(?:abc|def)"
(?P<name>…)

Diese Extension erzeugt eine Gruppe mit dem angegebenen Namen. Das Besondere an einer solchen benannten Gruppe ist, dass sie nicht allein über ihren Index, sondern auch über ihren Namen referenziert werden kann. Der Name muss ein gültiger Bezeichner sein:

r"(?P<hallowelt>abc|def)"
(?P=name)

Passt auf all das, auf das die bereits definierte Gruppe mit dem Namen name gepasst hat. Diese Extension erlaubt es also, eine benannte Gruppe zu referenzieren.

r"(?P<py>[Pp]ython) ist, wie (?P=py) sein sollte"

Dieser reguläre Ausdruck passt auf den String "Python ist, wie Python sein sollte".

(?#...)

Diese Extension stellt einen Kommentar dar. Der Inhalt der Klammern wird schlicht ignoriert:

r"Py(?#lalala)thon"
(?=…)

Passt nur dann, wenn der reguläre Ausdruck … als Nächstes passt. Diese Extension greift also vor, ohne in der Auswertung des Ausdrucks tatsächlich voranzuschreiten.

Diese Extension ist vor allem beim Searching von Bedeutung.

(?!…)

Passt nur dann, wenn der reguläre Ausdruck … als Nächstes nicht passt. Diese Extension ist das Gegenstück zu der vorherigen.

Diese Extension ist vor allem beim Searching von Bedeutung.

(?<=…)

Passt nur, wenn der reguläre Ausdruck … zuvor gepasst hat. Diese Extension greift also auf bereits ausgewertete Teile des Strings zurück, ohne die Auswertung selbst zurückzuwerfen.

Diese Extension ist vor allem beim Searching von Bedeutung.

(?<!...)

Passt nur, wenn der reguläre Ausdruck … zuvor nicht gepasst hat. Diese Extension ist damit das Gegenstück zu der vorherigen.

Diese Extension ist vor allem beim Searching von Bedeutung.

(?(id/name)yes-pattern|no-pattern)

Diese, recht kompliziert anmutende Extension kann in einem regulären Ausdruck als eine Art Fallunterscheidung verwendet werden. Abhängig davon, ob eine Gruppe mit dem angegebenen Index bzw. dem angegebenen Namen auf einen Teilstring gepasst hat, wird entweder (im positiven Fall) auf das yes-pattern oder (im negativen Fall) auf das no-pattern getestet. Das no-pattern wird durch einen senkrechten Strich vom yes-pattern getrennt, kann aber auch weggelassen werden. Vielleicht ist der Sinn dieser Extension noch nicht ganz klar geworden, deshalb folgendes Beispiel:

r"(?P<klammer>\()?Python(?(klammer)\))"

In diesem Ausdruck wird zunächst eine Gruppe namens klammer erstellt, die maximal einmal vorkommen darf und aus einer öffnenden, runden Klammer besteht. Danach folgt die Zeichenkette Python, und schlussendlich wird durch die Extension eine schließende Klammer gefordert, sofern zuvor eine öffnende aufgetreten ist, also sofern die Gruppe klammer zuvor gepasst hat.

Damit passt der reguläre Ausdruck auf die Strings "Python" und "(Python)", beispielsweise aber nicht auf "(Python".

Damit wäre den syntaktischen Regeln für reguläre Ausdrücke Genüge getan. Auch wenn dieser Abschnitt möglicherweise etwas trocken und theoretisch war, ist es durchaus wichtig, sich mit regulären Ausdrücken auseinanderzusetzen, denn in vielen Fällen ist der Einsatz regulärer Ausdrücke besonders elegant.

In den folgenden Abschnitten möchten wir über die praktische Anwendung regulärer Ausdrücke in Python reden. Dazu gehört zunächst einmal die Verwendung des Moduls re. Danach werden wir jeweils ein kleines Beispielprojekt zum Matching bzw. Searching bringen.


Galileo Computing - Zum Seitenanfang

15.2.2 Verwendung des Moduls  Zur nächsten ÜberschriftZur vorigen Überschrift

Nachdem Sie in die unendlichen Weiten der regulären Ausdrücke eingeführt wurden, werden wir uns hier um ihre konkrete Verwendung in Python kümmern. Die Beispiele dieses Abschnitts werden im interaktiven Modus durchgeführt und setzen voraus, dass das Modul re eingebunden wurde:

>>> import re

Flags

Im vorherigen Abschnitt wurden mehrfach die sogenannten Flags angesprochen. Das sind bestimmte Einstellungen, die die Auswertung eines regulären Ausdrucks beeinflussen. Flags können entweder im Ausdruck selbst durch eine Extension oder als Parameter einer der im Modul re verfügbaren Funktionen angegeben werden. Sie beeinflussen nur den Ausdruck, der aktuell verarbeitet wird, und verbleiben nicht nachhaltig im System. Jedes Flag ist als Konstante im Modul re enthalten und kann über eine Lang- oder eine Kurzversion seines Namens angesprochen werden. Die folgende Tabelle listet alle Flags auf und erläutert ihre Bedeutung.


Tabelle 15.6  Flags
Alias Name Bedeutung

re.I

re.IGNORECASE

Macht die Auswertung des regulären Ausdrucks case insensitive, das heißt, dass die Zeichengruppe [A-Z] sowohl auf Groß- als auch auf Kleinbuchstaben passen würde.

re.L

re.LOCALE

Gibt an, dass bestimmte vordefinierte Zeichenklassen von der aktuellen Lokalisierung abhängig gemacht werden sollen. Das betrifft die Gruppen \w, \W, \b, \B, \s und \S.

re.M

re.MULTILINE

Wenn dieses Flag gesetzt wurde, passt ^ sowohl zu Beginn des Strings als auch nach jedem Newline-Zeichen und $ vor jedem Newline-Zeichen.

Normalerweise passen ^ und $ nur am Anfang bzw. am Ende des Strings.

re.S

re.DOTALL

Wenn dieses Flag gesetzt wurde, passt das Sonderzeichen . tatsächlich auf jedes Zeichen. Normalerweise passt der Punkt auf jedes Zeichen außer auf das Newline-Zeichen \n.

re.U

re.UNICODE

Wenn dieses Flag gesetzt wurde, passen sich die vordefinierten Zeichenklassen dem Unicode-Standard an. Das heißt, dass dann auch Nicht-ASCII-Zeichen als Buchstabe oder Ziffer eingestuft werden.

re.X

re.VERBOSE

Das Setzen dieses Flags erlaubt es Ihnen, einen regulären Ausdruck zu formatieren. Wenn es gesetzt wurde, werden Whitespace-Zeichen wie Leerzeichen, Tabulatoren oder Newline-Zeichen ignoriert, solange sie nicht durch einen Backslash eingeleitet werden. Zudem leitet ein #-Zeichen einen Kommentar ein. Das heißt, alles hinter diesem Zeichen bis zu einem Newline-Zeichen wird ignoriert.


Funktionen

Neben den Flags sind im Modul re noch einige Funktionen enthalten, die im Folgenden besprochen werden sollen.

re.compile(pattern[, flags])

Kompiliert den regulären Ausdruck pattern zu einem Regular-Expression-Objekt, im Folgenden RE-Objekt genannt. Bei mehreren Operationen auf demselben regulären Ausdruck lohnt es sich, diesen zu kompilieren, da diese Operationen dann wesentlich schneller durchgeführt werden können. Zum Durchführen der Operationen bietet das RE-Objekt im Wesentlichen die gleiche Funktionalität wie das Modul re.

Um die Auswertung des Ausdrucks zu beeinflussen, können ein oder mehrere Flags angegeben werden. Wenn es sich um mehrere handelt, müssen diese durch das bitweise ODER | getrennt werden.

>>> c1 = re.compile(r"P[yY]thon") 
>>> c2 = re.compile(r"P[y]thon", re.I) 
>>> c3 = re.compile(r"P[y]thon", re.I | re.S)

Die Angabe von Flags ist bei den meisten Funktionen des Moduls re über den Parameter flags möglich. Wir werden darauf in Zukunft nicht mehr eingehen.

Näheres zum RE-Objekt folgt im nächsten Abschnitt.

re.search(pattern, string[, flags])

Durchsucht den String string nach einem Teilstring, auf den der reguläre Ausdruck pattern passt. Der erste gefundene Teilstring wird in Form eines sogenannten Match-Objekts zurückgegeben. Näheres zur Verwendung des Match-Objekts erfahren Sie im entsprechenden Abschnitt.

Wenn kein Ergebnis gefunden wurde, gibt die Funktion None zurück.

>>> re.search(r"P[Yy]thon", "Nimm doch Python") 
<_sre.SRE_Match object at 0xb7bd7f00>
re.match(pattern, string[, flags])

Wenn null oder mehr Zeichen am Anfang des Strings string auf den regulären Ausdruck pattern passen, wird diese Übereinstimmung in Form eines Match-Objekts zurückgegeben. Wenn keine Übereinstimmung gefunden wurde, wird None zurückgegeben.

>>> print re.match(r"P[Yy]thon", "PYYthon") 
None 
>>> re.match(r"P[Yy]thon", "PYthon") 
<_sre.SRE_Match object at 0xb7bd7f00>
re.split(pattern, string[, maxsplit])

Der String string wird nach Übereinstimmungen mit dem regulären Ausdruck pattern durchsucht. Alle passenden Teilstrings werden als Trennzeichen angesehen, und die dazwischenliegenden Teile werden als Liste von Strings zurückgegeben.

>>> re.split(r"\s", "Python Python Python") 
['Python', 'Python', 'Python']

Eventuell vorkommende Gruppen innerhalb des regulären Ausdrucks werden ebenfalls als Elemente dieser Liste zurückgegeben:

>>> re.split(r"\s(.*?)\s", "Python oder Python und Python") 
['Python', 'oder', 'Python', 'und', 'Python']

In diesem regulären Ausdruck werden alle von zwei Whitespaces umgebenen Wörter als Trennzeichen behandelt.

Wenn der Parameter maxsplit angegeben wurde und ungleich 0 ist, wird der String maximal maxsplit-mal unterteilt. Der Reststring wird als letztes Element der Liste zurückgegeben.

re.findall(pattern, string[, flags])

Sucht im String string nach Übereinstimmungen mit dem regulären Ausdruck pattern. Alle gefundenen, nicht überlappenden Übereinstimmungen werden in Form einer Liste von Strings zurückgegeben:

>>> re.findall(r"P[Yy]thon", "Python oder PYthon und Python") 
['Python', 'PYthon', 'Python']

Wenn pattern ein oder mehrere Gruppen enthält, werden diese anstelle der übereinstimmenden Teilstrings in die Ergebnisliste geschrieben.

>>> re.findall(r"P([Yy])thon", "Python oder PYthon und Python") 
['y', 'Y', 'y'] 
>>> re.findall(r"P([Yy])th(.)n", "Python oder PYthon und Python") 
[('y', 'o'), ('Y', 'o'), ('y', 'o')]

Bei mehreren Gruppen handelt es sich um eine Liste von Tupeln.

re.finditer(pattern, string[, flags])

Sucht im String string nach Übereinstimmungen mit dem regulären Ausdruck pattern. Das Ergebnis ist ein Iterator, der über alle gefundenen, nicht überlappenden Übereinstimmungen jeweils als Match-Objekt iteriert.

re.sub(pattern, repl, string[, flags])

Die Funktion sub sucht im String string nach nicht überlappenden Übereinstimmungen mit dem regulären Ausdruck pattern. Es wird eine Kopie des Strings string zurückgegeben, in dem alle passenden Teilstrings durch den String repl ersetzt wurden:

>>> re.sub(r"[Jj]a[Vv]a","Python", "Java oder java und jaVa") 
'Python oder Python und Python'

Statt eines Strings kann für repl auch ein Funktionsobjekt übergeben werden. Dieses wird für jede gefundene Übereinstimmung aufgerufen und bekommt das jeweilige Match-Objekt als einzigen Parameter. Der übereinstimmende Teilstring wird durch den Rückgabewert der Funktion ersetzt.

Es ist möglich, durch die Schreibweisen \g<name> oder \g<index> Gruppen des regulären Ausdrucks zu referenzieren:

>>> re.sub(r"([Jj]ava)","Python statt \g<1>", "Nimm doch Java") 
'Nimm doch Python statt Java'

Durch den optionalen Parameter count kann die maximale Anzahl an Ersetzungen festgelegt werden, die vorgenommen werden dürfen.

re.subn(pattern, repl, string[, flags])

Funktioniert ähnlich wie sub, mit dem Unterschied, dass ein Tupel zurückgegeben wird, in dem zum einen der neue String und zum anderen die Anzahl der vorgenommenen Ersetzungen stehen:

>>> re.subn(r"([Jj]ava)","Python statt \g<1>", "Nimm doch Java") 
('Nimm doch Python statt Java', 1)
re.escape(string)

Wandelt alle nicht-alphanumerischen Zeichen von string in ihre entsprechende Escape-Sequenz um und gibt das Ergebnis als String zurück. Diese Funktion ist besonders dann sinnvoll, wenn man einen String in einen regulären Ausdruck einbetten möchte, aber nicht sicher sein kann, ob Sonderzeichen, beispielsweise ein Punkt, enthalten sind.

>>> re.escape("Funktioniert das wirklich? ... (ja!)") 
'Funktioniert\\ das\\ wirklich\\?\\ \\.\\.\\.\\ \\(ja\\!\\)'

Beachten Sie, dass die Escape-Sequenzen im Stringliteral jeweils durch einen doppelten Backslash eingeleitet werden. Das liegt daran, dass das Ergebnis als String und nicht als Raw-String zurückgegeben wird.

Das Regular-Expression-Objekt

Ein Regular-Expression-Objekt, im Folgenden RE-Objekt genannt, wird erzeugt, wenn ein regulärer Ausdruck kompiliert wurde. Das Kompilieren eines regulären Ausdrucks ist sinnvoll, wenn mehrere Operationen mit ihm durchgeführt werden sollen. Diese können dann zusammengenommen wesentlich schneller durchgeführt werden, als wenn man die Funktionen match oder search direkt aufrufen würde.

Damit Searching- und Matching-Operationen mit einem kompilierten regulären Ausdruck durchgeführt werden können, besitzt das RE-Objekt eine Funktionalität, die deckungsgleich ist mit der des re-Moduls. Das bedeutet, dass für das RE-Objekt größtenteils die Funktionen des re-Moduls als Methoden implementiert sind, selbstverständlich mit gewissen Änderungen der Schnittstelle.

Wir werden hier nicht genau auf die Funktionsweise der Methoden eingehen, sondern nur einen Vergleich zu den Funktionen des re-Moduls ziehen. Dennoch ist es aufgrund der Änderungen bei den Schnittstellen wichtig, alle Methoden zu behandeln. Die Beispiele verstehen sich in folgendem Kontext:

>>> import re 
>>> c = re.compile(r"P[Yy]th.n")

Das bedeutet: Es existiert ein RE-Objekt namens c, dem der reguläre Ausdruck r"P[Yy]th.n" zugrunde liegt.

c.match(string[, pos[, endpos]])

Äquivalent zur Funktion re.match. Die optionalen Parameter pos und endpos geben, wenn sie ungleich 0 sind, zwei Indizes an, zwischen denen das Matching durchgeführt werden soll. Wenn sie nicht angegeben wurden, wird das Matching auf dem gesamten String durchgeführt.

>>> print c.match("Pythoon") 
None 
>>> c.match("Python") 
<_sre.SRE_Match object at 0xb7c49e58>
c.search(string[, pos[, endpos]])

Äquivalent zur Funktion re.search. Die optionalen Parameter pos und endpos haben dieselbe Bedeutung wie bei der Methode match.

>>> c.search("Dies ist Python") 
<_sre.SRE_Match object at 0xb7c49e58>
c.split(string[, maxsplit])

Äquivalent zur Funktion re.split.

>>> c.split("halloweltPythonhallowelt") 
['hallowelt', 'hallowelt']
c.findall(string[, pos[, endpos]])

Äquivalent zur Funktion re.findall. Die optionalen Parameter pos und endpos haben dieselbe Bedeutung wie bei der Methode match.

>>> c.findall("Python Python Python") 
['Python', 'Python', 'Python']
c.finditer(string[, pos[, endpos]])

Äquivalent zur Funktion re.finditer. Die optionalen Parameter pos und endpos haben dieselbe Bedeutung wie bei der Methode match.

c.sub(repl, string[, count])

Äquivalent zur Funktion re.sub.

c.subn(repl, string[, count])

Äquivalent zur Funktion re.subn.

Neben diesen Methoden enthält das RE-Objekt drei Attribute, die das Arbeiten mit dem Objekt erleichtern.

c.flags

Das Attribut flags ist eine ganze Zahl und enthält alle gesetzten Flags. Beachten Sie, dass Flags selbst auch ganze Zahlen sind und eine Kombination von Flags durch ihr bitweises ODER repräsentiert wird. Die zu setzenden Flags werden beim Erzeugen des RE-Objekts der Funktion re.compile übergeben. Wenn kein Flag gesetzt wurde, ist der Wert des Attributs 0.

>>> c.flags 
0

Um zu testen, ob ein bestimmtes Flag gesetzt ist, kann das bitweise UND verwendet werden:

>>> c1 = re.compile(r"P[Yy]th.n", re.I) 
>>> c1.flags 
2 
>>> c1.flags & re.I 
2 
>>> c1.flags & re.M 
0

Das bitweise UND zwischen dem Attribut flags und einem nicht gesetzten Flag ergibt immer 0.

c.groupindex

Das Attribut groupindex ist ein Dictionary, das alle Namen benannter Gruppen als Schlüssel enthält und die Indizes dieser Gruppen als Werte. Eine benannte Gruppe wird durch die Extension (?P<name>…) erzeugt.

>>> c2 = re.compile(r"(?P<gruppe1>P[Yy])(?P<gruppe2>th.n)") 
>>> c2.groupindex 
{'gruppe1': 1, 'gruppe2': 2}
c.pattern

Das Attribut pattern ist ein String und enthält den regulären Ausdruck, der dem RE-Objekt zugrunde liegt.

>>> c.pattern 
'P[Yy]th.n'

Das Match-Objekt

Nachdem wir das RE-Objekt besprochen haben, wenden wir uns einem wesentlich interessanteren Objekt zu, dem Match-Objekt. Eine solche Instanz wird zurückgegeben, wenn eine Match- oder Search-Operation Übereinstimmungen gefunden hat. Das Match-Objekt enthält nähere Details zu diesen gefundenen Übereinstimmungen.

Die Beispiele in diesem Unterkapitel verstehen sich in folgendem Kontext:

>>> import re 
>>> c = re.compile(r"(P[Yy])(th.n)")

Das Match-Objekt verfügt über folgende Methoden:

m.expand(template)

Die Methode expand erlaubt es, den String template mit Informationen zu füllen, die aus der Matching- bzw. Searching-Operation stammen. So können über \g<index> und \g<name> die Teilstrings eingefügt werden, die auf die jeweiligen Gruppen gepasst haben. Beachten Sie unbedingt, dass Sie template wegen der Backslashs als Raw-String angeben sollten.

>>> m = c.match("Python") 
>>> m.expand(r"Hallo \g<1> Welt \g<2>") 
'Hallo Py Welt thon'
m.group([group1, …])

Die Methode group erlaubt einen komfortablen Zugriff auf die Teilstrings, die auf die verschiedenen Gruppen des regulären Ausdrucks gepasst haben. Wenn nur ein Argument übergeben wurde, ist der Rückgabewert ein String, ansonsten ein Tupel von Strings. Wenn eine Gruppe auf keinen Teilstring gepasst hat, wird für diese None zurückgegeben. Ein Index von 0 gibt alle Gruppen zurück.

>>> m = c.match("Python") 
>>> m.group(0) 
'Python' 
>>> m.group(1) 
'Py' 
>>> m.group(1, 2) 
('Py', 'thon')
m.groups([default])

Gibt ein Tupel zurück, das alle Teilstrings enthält, die auf eine der im regulären Ausdruck enthaltenen Gruppen gepasst haben. Der optionale Parameter default erlaubt es, den Wert festzulegen, der in das Tupel geschrieben wird, wenn auf eine Gruppe kein Teilstring gepasst hat. Der Parameter ist mit None vorbelegt.

>>> m = c.match("Python") 
>>> m.groups() 
('Py', 'thon')
m.groupdict([default])

Gibt ein Dictionary zurück, das die Namen aller benannten Gruppen als Schlüssel und die jeweils passenden Teilstrings als Werte enthält. Der Parameter default hat die gleiche Bedeutung wie bei der Methode groups.

>>> c2 = re.compile(r"(?P<gruppe>P[Yy])(th.n)") 
>>> m2 = c2.match("Python") 
>>> m2.groupdict() 
{'gruppe': 'Py'}
m.start([group]), end([group])

Gibt den Start- bzw. Endindex des Teilstrings zurück, der auf die Gruppe group gepasst hat. Der optionale Parameter group ist mit 0 vorbelegt.

m = c.match("Python") 
>>> m.start(2) 
2 
>>> m.end(2) 
6
m.span([group])

Gibt das Tupel (start(group), end(group)) zurück.

>>> m = c.match("Python") 
>>> m.span(2) 
(2, 6)

Neben den soeben beschriebenen Methoden besitzt das Match-Objekt sechs Attribute, die im Folgenden beschrieben werden sollen.

m.pos, m.endpos

Die Methoden match und search des RE-Objekts besitzen zwei Parameter namens pos und endpos. Die Attribute pos und endpos des Match-Objekts erlauben den Zugriff auf die dort zuletzt übergebenen Werte.

m.lastindex

Der Index der Gruppe, die bei der Auswertung als Letzte auf einen Teilstring gepasst hat, oder None, wenn keine Gruppe gepasst hat.

m.lastgroup

Der Name der symbolischen Gruppe, die bei der Auswertung als Letzte auf einen Teilstring gepasst hat, oder None, wenn keine Gruppe gepasst hat.

m.re

Der ursprüngliche reguläre Ausdruck als String.

m.string

Der String, der der match- bzw. search-Methode des RE-Objekts zuletzt übergeben wurde.


Galileo Computing - Zum Seitenanfang

15.2.3 Ein einfaches Beispielprogramm – Searching  Zur nächsten ÜberschriftZur vorigen Überschrift

Bisher wurde sowohl die Syntax regulärer Ausdrücke als auch deren Verwendung durch das Modul re der Standardbibliothek besprochen. Eigentlich ist die Thematik damit erschöpfend behandelt, doch wir möchten an dieser Stelle zwei kleine Beispielprojekte vorstellen, die stark auf reguläre Ausdrücke setzen, um auch einer praxisorientierten Einführung gerecht zu werden. Zunächst soll in diesem relativ einfach gehaltenen Programm das Searching und im nächsten, etwas komplexeren Beispiel das Matching erklärt werden.

Mithilfe des Searchings können Muster innerhalb eines längeren Textes gefunden und herausgefiltert werden. In unserem Beispielprogramm soll das Searching dazu genutzt werden, alle Links aus einer beliebigen HTML-Datei mitsamt Beschreibung herauszulesen. Dazu müssen wir uns zunächst den Aufbau eines HTML-Links vergegenwärtigen:

<a href="URL">Beschreibung</a>

Dazu ist zu sagen, dass HTML nicht zwischen Groß- und Kleinschreibung unterscheidet, wir den regulären Ausdruck also mit dem IGNORECASE-Flag verwenden sollten. Des Weiteren handelt es sich bei dem obigen Beispiel um die einfachste Form eines HTML-Links, denn neben der URL und der Beschreibung können noch weitere Angaben getätigt werden.

Der folgende reguläre Ausdruck passt sowohl auf den oben beschriebenen als auch auf weitere, komplexere HTML-Links:

r"<a.*href=[\"\'](.*?)[\"\'].*>(.*?)</a>"

Wichtig ist, dass der reguläre Ausdruck zwei Gruppen enthält, jeweils für die URL und die Beschreibung, sodass diese beiden Angaben später bequem ausgelesen werden können. Außerdem sollten Sie unbedingt beachten, dass innerhalb dieser Gruppen »genügsame« Quantoren eingesetzt wurden, da sonst mehrere Links fälschlicherweise zu einem zusammengefasst werden könnten.

Doch nun zum Beispielprogramm:

import re
f = open("test.html", "r") html = f.read() f.close()
it = re.finditer(r"<[a].*href=[\"\'](.*?)[\"\'].*>(.*?)</[a]>", html, re.I) for m in it: print "Name: %s, Link: %s" % (m.group(2), m.group(1))

Zunächst wird eine beliebige HTML-Datei, in diesem Fall test.html, geöffnet und mithilfe der Methode read des Dateiobjekts ausgelesen. Danach wird die Funktion finditer des Moduls re aufgerufen, um alle Übereinstimmungen mit dem vorhin besprochenen regulären Ausdruck im HTML-Code zu finden. Das Ergebnis wird als Iterator zurückgegeben und von it referenziert.

Schlussendlich wird über it iteriert. In jedem Iterationsschritt ist die aktuelle Übereinstimmung als Match-Objekt m verfügbar. Jetzt werden nur noch die Teilstrings ausgegeben, die auf die beiden Gruppen des regulären Ausdrucks gepasst haben.

Sie können das Programm mit beliebigen HTML-Seiten testen. Besuchen Sie dazu im Internet eine möglichst komplexe Website, beispielsweise die eines Nachrichtenmagazins, und speichern Sie diese als HTML-Datei ab. Sie werden sehen, dass das Beispielprogramm auch hier die enthaltenen Links findet.

Das hier vorgestellte Programm schreit geradezu danach, erweitert zu werden. Beispielsweise könnten neben Links noch andere Teile des HTML-Codes, wie enthaltene Bilder oder Überschriften, ausgelesen werden.


Galileo Computing - Zum Seitenanfang

15.2.4 Ein komplexeres Beispielprogramm – Matching  topZur vorigen Überschrift

Es ist allgemein – und besonders im Web – ein häufiges Problem, eingegebene Formulardaten zu validieren und die wichtigen Informationen aus den Eingaben herauszufiltern. Dies ist selbstverständlich auch mit normalen String-Operationen möglich, doch mutiert der Code bei solchen Versuchen schnell zu einem unförmigen Batzen von Irgendwas. Das angesprochene Problem lässt sich durch reguläre Ausdrücke sehr elegant und nur mit geringem Quellcodeaufwand lösen. Unser Beispielprogramm soll aus einer Art elektronischer Visitenkarte alle relevanten Informationen auslesen und maschinenlesbar aufbereiten. Die Visitenkarte ist in einer Textdatei in folgendem Format gespeichert:

Name: Max Mustermann 
Addr: Musterstr 123 
      12345 Musterhausen 
Tel:  +49 1234 56789

Das Programm soll nun diese Textdatei einlesen, die enthaltenen Informationen extrahieren und zu einem solchen Dictionary aufbereiten:

{ 
'Tel': ('+49', '1234', '56789'), 
'Name': ('Max', 'Mustermann'), 
'Addr': ('Musterstr', '123', '12345', 'Musterhausen') 
}

In der Textdatei soll dabei immer nur ein Datensatz stehen.

Zunächst einmal möchten wir etwas detaillierter auf die Funktionsweise des Beispielprogramms eingehen. Die Visitenkarte besteht aus verschiedenen Informationen, denen immer eine Überschrift bzw. Kategorie gegeben wurde (»Name«, »Addr« und »Tel«). Die Kategorie von der Information zu trennen ist keine komplizierte Angelegenheit, da der Doppelpunkt innerhalb der Informationen nicht vorkommt und somit in jeder Zeile einzigartig ist. Ein Problem ist die dritte Zeile, da hier keine explizite Überschrift gegeben ist. In einem solchen Fall wird die Zeile an die Information der vorherigen Überschrift angehängt. Auf diese Weise lässt sich ein Dictionary erzeugen, das die Überschriften auf die jeweiligen Informationen mappt.

Die Informationen werden allerdings zeilenweise aus der Datei ausgelesen. Das ist nicht optimal, da wir die Daten ausdrücklich maschinenlesbar einlesen wollten, das heißt insbesondere nach Einzelinformationen getrennt. Für diese Arbeit bieten sich reguläre Ausdrücke förmlich an.

Kommen wir zur konkreten Implementierung. Dazu schreiben wir zunächst eine Funktion, die die Daten zeilenweise einliest und zu einem Dictionary aufbereitet:

def leseDatei(datei): 
    d = {} 
    f = open(datei) 
    for zeile in f: 
        if ":" in zeile: 
            key, d[key] = (s.strip() for s in zeile.split(":")) 
        elif "key" in locals(): 
            d[key] += "\n%s" % zeile.strip() 
    f.close() 
    return d

Die Funktion leseDatei bekommt den String datei mit einer Pfadangabe übergeben. Innerhalb der Funktion wird die Datei zeilenweise eingelesen. Jede Zeile wird anhand des Doppelpunktes in die beiden Teile »Überschrift« und »Information« aufgeteilt und, durch Einsatz der Methode strip, von überflüssigen Leerzeichen befreit. Danach werden Überschrift und Information in das Dictionary d geschrieben und die jeweils aktuelle Überschrift zusätzlich durch key referenziert.

Wenn in einer Zeile kein Doppelpunkt vorkommt, wurde die Information auf mehrere Zeilen umgelegt. Das bedeutet für uns, dass wir zunächst auch die Methode strip auf den kompletten Zeileninhalt anwenden und sie dann unter der Überschrift key an den bereits bestehenden Wert im Dictionary anhängen. Damit dieses durchgeführt werden kann, muss die Referenz key selbstverständlich existieren. Da diese erst innerhalb der if-Anweisung angelegt wird, wird vorausgesetzt, dass eine Zeile mit Doppelpunkt vor einer Zeile ohne Doppelpunkt kommen muss. Obwohl es keine syntaktisch sinnvolle Datei gibt, in der das nicht gilt, überprüfen wir im elif-Zweig explizit, ob die Referenz key existiert.

Das Resultat dieser Funktion ist ein Dictionary mit den Überschriften als Schlüssel und den dazugehörigen Informationen (in Form von Strings) als Werte. Die zweite Funktion des Beispiels soll die Daten mithilfe regulärer Ausdrücke analysieren und dann als Tupel im Dictionary ablegen. Dazu erzeugen wir zunächst ein Dictionary namens regexp, das für jede Überschrift einen regulären Ausdruck bereitstellt, der verwendet werden kann, um die Information zu validieren:

regexp = { 
         "Name" : r"([A-Za-z]+)\s([A-Za-z]+)", 
         "Addr" : r"([A-Za-z]+)\s(\d+)\s*(\d{5})\s([A-Za-z]+)", 
         "Tel"  : r"(\+\d{2})\s(\d{4})\s(\d{3,})" 
         }

Diese regulären Ausdrücke verfügen über mehrere Gruppen, um das Aufteilen der Information in die verschiedenen Einzelinformationen zu erleichtern.

Die Funktion, mit der die Daten analysiert werden, sieht folgendermaßen aus:

def analysiereDaten(daten, regexp): 
    for key in daten: 
        m = re.match(regexp[key], daten[key]) 
        if not m: 
            return False 
        daten[key] = m.groups() 
    return True

Die Funktion analysiereDaten bekommt zwei Dictionarys als Parameter übergeben: zum einen das soeben erstellte Dictionary regexp und zum anderen das, das von der vorherigen Funktion erstellt wurde und die eingelesenen Daten enthält.

Die Funktion iteriert in einer for-Schleife über das Dictionary daten und wendet, jeweils passend zur aktuellen Überschrift, mithilfe der Funktion re.match den regulären Ausdruck auf den eingelesenen String an. Das zurückgegebene Match-Objekt wird durch m referenziert.

Nachfolgend wird getestet, ob re.match None zurückgegeben hat. Ist das der Fall, gibt die Funktion analysiereDaten ihrerseits False zurück. Andernfalls wird der aktuelle Wert des Dictionarys daten mit den Teilstrings überschrieben, die auf die einzelnen Gruppen der regulären Ausdrücke gepasst haben. Die Methode group des Match-Objekts gibt ein Tupel von Strings zurück. Nach dem Durchlaufen der Funktion analysiereDaten enthält das Dictionary die gewünschten Daten in aufbereiteter Form.

Zu guter Letzt fehlt noch der Code, der den Anstoß zum Einlesen und Aufbereiten der Daten gibt:

daten = leseDatei("id.txt") 
if analysiereDaten(daten, regexp): 
    print daten 
else: 
    print "Die Angaben sind fehlerhaft"

Je nachdem, welchen Wahrheitswert die Funktion analysiereDaten zurückgegeben hat, werden die aufbereiteten Daten oder eine Fehlermeldung ausgegeben.

Hoffentlich haben Ihnen die beiden Beispiele geholfen, einen praxisbezogenen Einstieg in die Welt der regulären Ausdrücke zu finden. Bleibt noch zu sagen, dass das dargebotene Programm zwar funktioniert, aber nicht perfekt ist. Fühlen Sie sich dazu ermutigt, es zu erweitern oder anzupassen. So erlauben die regulären Ausdrücke beispielsweise noch keine Umlaute oder Interpunktionszeichen im Straßennamen. Sie könnten beispielsweise auch Visitenkarte und Programm um die Angabe einer E-Mail-Adresse erweitern.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






 <<   zurück
  
  Zum Katalog
Zum Katalog: Python






Python
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Linux






 Linux


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


Zum Katalog: Praxisbuch Web 2.0






 Praxisbuch Web 2.0


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Praxisbuch Objektorientierung






 Praxisbuch Objektorientierung


Zum Katalog: Einstieg in SQL






 Einstieg in SQL


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo