Durch das Matching gesteuerte Spezialvariablen

(Auszug aus "Reguläre Ausdrücke" von Jeffrey E. F. Friedl)

Ein erfolgreiches Matching oder eine Substitution setzt eine Reihe von speziellen Variablen, die einen automatisch erzeugten dynamischen Geltungsbereich haben und nur gelesen werden können (read-only). Diese Variablen ändern sich nie, wenn eine Mustersuche fehlschlägt, und sie werden immer gesetzt, wenn ein Treffer gefunden wird. Möglicherweise werden sie nur auf den Leerstring (einen String der Länge null) gesetzt oder auch auf den undefinierten Wert (dieser ist ähnlich, aber durchaus verschieden vom Nullstring; er bedeutet so etwas wie »kein Wert hier«). Aber die Variablen werden bei einem Treffer alle verändert. In der folgenden Tabelle sind diese Spezialvariablen aufgeführt.

Tabelle: Beispiel für die Spezialvariablen, die nach der Mustersuche zur Verfügung stehen.

Von der folgenden Mustersuche gesetzte Spezialvariablen
"Pi ist 3.14159, ungefähr." =~ m/\b(1(2schmackhaft|fettig)2|(3\d+(4\.\d*)4?)3)1\b/;
Variable Bedeutung Wert
$` Text vor dem Treffer. Pi ist
$& Text des Treffers. 3.14159
$' Text nach dem Treffer. , ungefähr.
$1 Text, der vom 1. Klammerpaar eingefangen wurde. 3.14159
$2 Text, der vom 2. Klammerpaar eingefangen wurde. undef
$3 Text, der vom 3. Klammerpaar eingefangen wurde. 3.14159
$4 Text, der vom 4. Klammerpaar eingefangen wurde. .14159
$+ Wert der höchstnummerierten Variablen $1, $2 usw., die am Treffer beteiligt war. .14159
$^N Text aus der zuletzt geschlossenen Klammer von $1, $2 usw. 3.14159
@- Array mit Offsets zu den Anfängen der Sub-Treffer $1, $2 usw. (7, 7, undef, 7, 8)
@+ Array mit Offsets zu den Enden der Sub-Treffer $1, $2 usw. (14, 14, undef, 14, 14)

Das Folgende ist eine etwas ausführlichere Darstellung der Spezialvariablen, die nach einer erfolgreichen Mustersuche zur Verfügung stehen:

$&

Der Text bzw. eine Kopie des Textes, auf den die Regex als Ganzes gepasst hat. Diese Variable sollte (wie auch $' und $`) möglichst vermieden werden (mehr dazu bei Die Variablen $`, $& und $' sind »unartig«). $& ist nach einem erfolgreichen Treffer nie undefiniert, kann aber den Leerstring enthalten.

$`

Eine Kopie des Textes vor dem Treffer (links davon). Im Zusammenhang mit /g wünscht man sich oft, dass sich $` auf den Text seit dem letzten Versuch bezöge; leider ist das nicht so. $` ist nach einem erfolgreichen Treffer nie undefiniert.

$'

Eine Kopie des Textes nach dem Treffer (rechts davon). Nach einem erfolgreichen Treffer ist der String "$`$&$'" immer eine Kopie des ursprünglichen Suchtextes. (Anmerkung: Um genau zu sein: Wenn der Suchtext undefiniert und das Matching dennoch erfolgreich war (unwahrscheinlich, aber möglich), dann ist "$`$&$'" der leere String, nicht der undefinierte Wert. Das ist die einzige Situation, in der sich die beiden unterscheiden.) Auch $' ist nach einem erfolgreichen Treffer nie undefiniert.

$1, $2, $3 ...

Der Text, auf den das erste, zweite, dritte ... Klammerpaar gepasst hat. (Achtung: $0 gehört nicht in diese Reihe. $0 enthält den Namen des Skripts und hat überhaupt nichts mit regulären Ausdrücken zu tun.) Diese sind garantiert undefiniert, wenn sie sich auf ein Klammerpaar beziehen, das es in der Regex gar nicht gibt oder das bei der letzten Mustersuche nicht Bestandteil des Treffers war.

Diese Variablen können nach der Mustersuche benutzt werden, auch im Ersatztext des s/.../.../-Operators. Sie können auch in einem Codemuster oder in einem dynamischen Regex-Konstrukt benutzt werden (siehe Das dynamische Regex-Konstrukt). Sie in der Regex selbst zu benutzen ist kaum sinnvoll (dafür sind ˹\1˼ und seine Kollegen da). Siehe den Abschnitt $1 in der Regex benutzen?.

Den Unterschied zwischen ˹(\w+ und ˹(\w)+˼ erfasst man am besten, wenn man $1 anschaut. Beide Ausdrücke passen auf exakt den gleichen Text, aber was von den Unterausdrücken in Klammern eingefangen wird, ist verschieden. Auf tubby angewendet, wird $1 im ersten Fall zu tubby, im zweiten zu y: Das Plus befindet sich hier außerhalb der Klammern, damit wird $1 bei jeder Iteration des Pluszeichens erneut mit genau einem Zeichen gefüllt.

Ein anderer Unterschied besteht zwischen ˹(x)?˼ und ˹(x?)˼. Beim ersten Ausdruck sind die Klammern und das, was sie einschließen, optional, damit muss $1 entweder undefiniert oder ein ›x‹ sein. Im zweiten Fall umschließen die Klammern einen lokalen Treffer – wenn die Regex als Ganzes erfolgreich ist, passt auch der Klammerausdruck, vielleicht auch nur auf »gar nichts«, das ist bei ˹x?˼ ja möglich. Bei ˹(x?)˼ sind daher die möglichen Werte für $1 der String x oder der leere String.

Die folgende Tabelle gibt dazu ein paar Beispiele.

Matching $1 Matching $1
"::" =~ m/:(A?):/ Leerstring "::" =~ m/:(\w*):/ Leerstring
"::" =~ m/:(A)?:/ undef "::" =~ m/:(\w)*:/ undef
":A:" =~ m/:(A?):/ A ":Wort:" =~ m/:(\w*):/ Wort
":A:" =~ m/:(A)?:/ A ":Wort:" =~ m/:(\w)*:/ t

Wenn wie hier Klammern allein zum Einfangen von Text gesetzt werden, liegt die Entscheidung ganz bei Ihnen. In den obigen Beispielen haben die Klammern keinen Einfluss auf den Gesamttreffer (alle finden den gleichen Treffer), der Unterschied liegt nur im Nebeneffekt, d.h. in dem Text, der in $1 abgespeichert wird.

$+

Eine Kopie der Variablen $1, $2... mit der höchsten Nummer, die explizit gesetzt wurde. In einer Situation wie der folgenden

$url =~ m{
   href \s* = \s*     # Zuerst der »href =«-Teil, dann ...
   (?: "([^"]*)"      # ein String in Anführungszeichen oder ...
     | '([^']*)'      # ein String in Hochkommas oder ...
     | ([^'"<>]+))    # ein ›nackter‹ Wert.
}ix;

kann man damit sehr einfach den Wert des href-Verweises herausholen. Ohne $+ müsste man $1, $2 und $3 der Reihe nach testen und den Wert nehmen, der nicht undef ist.

Wenn in der Regex keine Klammer vorkommt (oder wenn am gefundenen Treffer kein Klammerausdruck Anteil hat), dann ist $+ undefiniert.

$^N

Eine Kopie der zuletzt abgespeicherten Variablen $1, $2 usw. Mit anderen Worten: der Wert der Spezialvariablen $1, $2 usw., der der Klammer entspricht, die beim Vorgang der Mustersuche zuletzt verlassen wurde. Wenn in der Regex keine Klammer vorkommt (oder wenn am gefundenen Treffer kein Klammerausdruck Anteil hat), dann ist $^N undefiniert. Ein gutes Anwendungsbeispiel für $^N finden Sie im Abschnitt Benannte Unterausdrücke imitieren.

@- und @+

Diese Arrays enthalten die String-Indizes (Offsets im Suchstring), an denen ein Treffer oder Teil-Treffer beginnt bzw. endet. Wegen der merkwürdigen Namen ist ihr Gebrauch etwas gewöhnungsbedürftig. Das erste Element bezieht sich auf den Gesamttreffer. Das erste Element von @-, nämlich $-[0], enthält den Index des Suchstrings, an dem der Treffer beginnt. Nach dem Matching

$text = "Version 6 kommt bald raus?";
  .
  . 
  .
$text =~ m/\d+/;

enthält $-[0] den Wert 8, der Treffer beginnt also beim achten Zeichen im Text (in Perl werden Indizes ab Null gezählt).

Das erste Element von @+, nämlich $+[0], ist der Index des Endes des Treffers. Bei diesem Beispiel ist das der Wert 9, der Treffer endet also nach dem neunten Zeichen. Mit substr($text, $-[0], $+[0] - $-[0]) erhält man so den Text des Treffers (sofern $text nicht verändert wurde). So kann man $& imitieren und vermeidet die Probleme bezüglich Effizienz, die mit $& verbunden sind (siehe Die Variablen $`, $& und $' sind »unartig«). Hier sehen Sie ein einfaches Beispiel für den Gebrauch von @-:

1 while $zeile =~ s/\t/' ' x (8 - $-[0] % 8)/e;

Dieses Programmstück ersetzt in einer Textzeile Tabulatoren durch die entsprechende Anzahl Leerzeichen. (Anmerkung: Das Programmstück funktioniert nur mit »normalem« Text aus einer westlichen Sprache. Es versagt bei Zeichen wie 柇, die aus mehreren Bytes aufgebaut, aber doch nur ein Zeichen breit sind, auch bei akzentuierten Unicode-Zeichen wie à, falls sie aus einem Grundzeichen und einem kombinierenden Zeichen aufgebaut sind; mehr dazu bei Unicode.)

Die weiteren Elemente der zwei Arrays beziehen sich auf die Start- und End-Indizes der von den Klammern erkannten Teil-Treffer. Das Paar $-[1] und $+[1] bezeichnet die Offsets von $1 im Suchstring, das Paar $-[2] und $+[2] die von $2 usw.

$^R

Diese Variable ist nur zusammen mit Codemustern in der Regex oder mit dynamischen Regex-Konstrukten relevant (siehe Das dynamische Regex-Konstrukt). Sie enthält den Rückgabewert des zuletzt ausgeführten Codekonstrukts. Bei Codemustern, die im if-Teil eines bedingten Klammerausdrucks erscheinen, also bei ˹(? if then|else)˼ (siehe Bedingte reguläre Ausdrücke), wird $^R nicht gesetzt. Der Wert wird mit den gespeicherten Zuständen abgelegt. Wenn also ein einmal ausgeführtes Codestück durch Backtracking dennoch nicht Teil des Gesamttreffers wird, wird auch der damit gespeicherte Rückgabewert verworfen. Anders gesagt, enthält $^R den »letzten« Rückgabewert aus einem Codestück, das zum Treffer geführt hat.

Wenn eine Regex mit dem /g-Modifikator wiederholt angewendet wird, werden alle diese Variablen neu gesetzt. Wegen dieser Eigenschaft kann man im Ersatztext von s/.../.../g durchaus $1 benutzen und erhält den eingefangenen Text der letzten Iteration.

$1 in der Regex benutzen?

In der Perl-Dokumentation wird immer wieder darauf hingewiesen, dass man ˹\1˼ außerhalb einer Regex nicht verwenden soll (sondern stattdessen $1). Die Variable $1 enthält den (statischen) Text, der vom entsprechenden Klammerausdruck der letzten Mustersuche aufgefangen wurde. ˹\1˼ ist dagegen ein echtes Regex-Metazeichen, das den gleichen Text erkennt, den der Unterausdruck in der entsprechenden Klammer weiter vorn in der Regex erkannt hat, und zwar zu dem Zeitpunkt, wenn die NFA-Maschine das ˹\1˼ erreicht. Dieser vom ersten Klammerpaar erkannte Text kann sich während des Vorgangs der Mustersuche durch das Backtracking ändern.

Umgekehrt kann man sich fragen, ob man $1 oder eine der anderen Spezialvariablen aus der Tabelle Beispiel für die Spezialvariablen, die nach der Mustersuche zur Verfügung stehen in einem Regex-Operanden verwenden kann. Man kann, und oft werden sie in einem Codemuster oder in dynamischen Regex-Konstrukten (siehe Das dynamische Regex-Konstrukt) verwendet. In den anderen Fällen ist es weniger sinnvoll. Die Variable $1 wird in einem Regex-Literal verwendet wie jede andere Variable auch: Ihr Wert wird interpoliert, bevor die eigentliche Regex-Maschine überhaupt in Gang gesetzt wird. Eine solchermaßen verwendete Variable hat also mit der aktuellen Mustersuche nichts zu tun, sie ist einfach ein Überbleibsel der letzten Mustersuche.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Reguläre Ausdrücke bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:
   

Copyright der deutschen Ausgabe © 2008 by O’Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Reguläre Ausdrücke" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstr. 81, 50670 Köln