Reguläre Ausdrücke in Perl (4)

## Klammerung von Ausdrücken

Im Abschnitt zur Mehrfachsuche wirkte eine entsprechende Anweisung immer nur auf das eine unmittelbar davor stehende Zeichen. Mit Hilfe von Klammern ("`(...)`") können auch längere Ausdrücke mehrfach gesucht werden.

Dazu ein Beispiel:

    #!/usr/local/bin/perl -w

    $t = "1.030303";

    if($t =~ /03{2,3}/)   { print "true ($&)\n" } else { print "false\n" }  # Test 1
    if($t =~ /(03){2,3}/) { print "true ($&)\n" } else { print "false\n" }  # Test 2

Test 1 liefert hier "`false`" (es wird nach einer '0' gefolgt von 2 oder 3 '3'en gesucht). Erfolgreich dagegen verläuft Test 2: er findet '030303' (längste Lösung).

Klammern bewirken auch noch einen Nebeneffekt: Derjenige Substring, der auf das Muster der ersten Klammer paßt, wird in die Variable `$1` geschrieben, der zweite Substring in `$2`, usw. Zur Festlegung der Reihenfolge der Klammern zählt die jeweils öffnende Klammer (dies ist wichtig bei ineinander verschachtelten Klammerpaaren).

    #!/usr/local/bin/perl -w

    $t = "Breite: 7.5 m";

    if($t =~ /(\w+): ([\d.]+ m)/) { print "true ($&)\n" } else { print "false\n" }

    print "\$1: $1\n";
    print "\$2: $2\n";

Auch dieser recht kompliziert aussehende reguläre Ausdruck läßt sich relativ einfach auflösen:

- `(\w+)` sucht nach mindestens einem alphanumerischen Zeichen und schreibt ggf. den gefundenen Substring in die Variable `$1`.
- anschließend wird nach Doppelpunkt und Leerzeichen gesucht
- `([\d.]+ m)` sucht nach mindestens einem Zeichen aus der Gruppe "Ziffern und Punkt" gefolgt von einem Leerzeichen und dem Buchstaben 'm'; das Ergebnis landet ggf. in `$2`.

Somit enthält schließlich `$1` den Wert 'Breite' und `$2` den Wert '7.5 m'. Da alle Bedingungen der Suche erfüllt sind, ergibt sich natürlich der Gesamtwert "`true`".

Möchte man den Nebeneffekt der Zuweisung von `$1`, `$2`, `$3`,... verhindern, fügt man nach der öffnenden Klammer "`?:`" ein.

    #!/usr/local/bin/perl -w

    $t = "a b";

    $t =~ /(.)\s(.)/;
    print "\$1: $1\n";      # gibt 'a' aus

    $t =~ /(?:.)\s(.)/;
    print "\$1: $1\n";      # gibt 'b' aus

Im ersten Test werden sowohl `$1` (mit 'a') als auch `$2` (mit 'b') belegt. Im zweiten Test dagegen findet bei der ersten Klammer keine Zuweisung statt und der Inhalt der zweiten Klammer ('b') wird in `$1` geschrieben.

Es ist sogar möglich, schon innerhalb des regulären Ausdrucks auf vorher im String gefundene Teilstrings zuzugreifen. So findet sich der Wert der ersten Klammer in `\1`, der der zweiten Klammer in `\2`, usw. Die Zuordnung ist identisch zu der von `$1`, `$2`, usw., allerdings dürfen die 'Backslash-Variablen' nur innerhalb des Suchmusters und die 'Dollar-Variablen' nur außerhalb verwendet werden.

    #!/usr/local/bin/perl -w

    $t = "abc--defg-h----ijk";

    if($t =~ /(.{2}).+?\1/) { print "true ($&)\n" } else { print "false\n" }
    print "\$1: $1\n";

Der reguläre Ausdruck sucht nach genau zwei gleichen Zeichen, die aber ansonsten beliebig gewählt sein können, gefolgt von einer möglichst kurzen Zeichenkette aus mindestens einem Symbol gefolgt von dem Zeichenpaar, das in der ersten Klammer gefunden wurde. Der gefundene Substring lautet dann '--defg-h--' und `$1` enthält wie erwartet '--'.

Die Verwendung von `\1`, `\2`, usw. kann zu Problemen führen, wenn sich daran eine Suche nach Ziffern anschließt; eine mögliche Lösung ist die Benutzung von Klammern des Typs "`(?:...)`":

    #!/usr/local/bin/perl -w

    $t = "12--456--12";

    if($t =~ /(--)\d+?\112/)     { print "true ($&)\n" }       # Test 1
                            else { print "false\n" }
    if($t =~ /(--)\d+?(?:\1)12/) { print "true ($&)\n" }       # Test 2
                            else { print "false\n" }

Hier schlägt Test 1 fehl, da `\112` als Oktal-Code interpretiert wird (Zeichen 'J'). Die zusätzliche Klammer in Test 2 sorgt für die korrekte Lösung ('--456--12').

Autor: Eike Grote | Letzte Änderung: 03.11.2004