Reguläre Ausdrücke bei VIM


Eine Einführung

Motivation

Das Suchen und Ersetzen von Zeichenketten basiert bei allen Vi-ähnlichen Editoren, zu denen natürlich der VIM gehört, auf regulären Ausdrücken, deren detaillierte und anfänglich vielleicht etwas verwirrende Defintion erst weiter unten folgt. Bevor wir uns in Einzelheiten verlieren, soll es um die recht einfach zu beschreibende Grundidee gehen. Reguläre Ausdrücke stellen Muster zur kompakten Beschreibung einer Menge von Zeichenketten dar. An einem Beispiel dürfte schnell klar werden, was sich dahinter verbirgt.

Betrachten wir Variablennamen in typischen Hochsprachen wie Pascal und C. Es handelt sich dabei gewöhnlich um Zeichenfolgen, die aus einem Buchstaben oder Underscore (_) bestehen, dem beliebig viele Buchstaben, Ziffern und Underscores folgen können. Das ist zwar weder aufregend noch neu, dafür aber eine konkrete Anwendung eines, wenn auch nur verbal formulierten regulären Ausdrucks zur Syntaxspezifikation. Die Menge der Zeichenketten, die diesem einfachen Muster entsprechen, ist unendlich, so dass sie nicht durch die Aufzählung aller ihrer Elemente angegeben werden kann. Unter Verwendung des genannten Musters ist die Lösung unseres Problem allerdings mehr als trivial.

Vielleicht lässt sich hier schon die Mächtigkeit des Konzepts erahnen. Um nun die ihm innewohnenden Potenzen nutzen zu können, ist natürlich etwas Arbeit zu investieren, die aber nicht gescheut werden sollte, da als Lohn der Zugang zur faszinierenden Welt der regulären Ausdrücke winkt, die auch "alte Hasen" gelegentlich recht ordentlich verblüfft.

Was ist nun konkret Sache? Die Theoretiker beantworten diese Frage mit der Feststellung, dass sich mittels regulärer Ausdrücke genau die regulären Sprachen beschreiben lassen, also diejenigen, denen lineare Grammatiken zugrunde liegen und die genau von den endlichen Automaten akzeptiert werden. Punkt. Das ist korrekt, trägt aber kaum zur Demystifizierung der Angelegenheit bei. Aber keine Angst. Es ist zum Glück nicht nötig, diesen Satz zu verstehen oder gar tiefer in die Automatentheorie bzw. die Theorie der formalen Sprachen einzudringen. Wie so oft im Leben geht es auch einfacher und praxisorientierter.

Vor der Diskussion der konkret bei VIM 5.3 zur Verfügung stehenden regulären Ausdrücke sei darauf hingewiesen, dass es, speziell im UNIX-Umfeld, neben Vi & Co. viele Werkzeuge und Sprachen gibt, die reguläre Ausdrücke unterstützen. Zu ihnen zählen z.B. grep / egrep, lex, ed, sed, awk, Perl, Tcl und Python.

Um es dem Anwender nicht ganz so einfach zu machen, unterscheiden sie sich jeweils mehr oder minder geringfügig hinsichtlich des angebotenen Leistungsumfangs und der verwendeten Syntax :-). Davon sollte man sich nicht weiter beeindrucken oder gar abschrecken lassen. Wenn einmal das Prinzip klar ist, reichen in der Regel einige Versuche am betreffenden Objekt aus, um die gewünschten Resultate zu erzielen.

Trial and error ist eben oft ein brauchfähiger Weg. Der VIM kann dabei als eine sehr gute, weil interaktive Übungsumgebung angesehen werden. Geht mal was schief, ist das kein Desaster. Nach einem Undo ist ja der alte Zustand wieder da, und ein neuer Versuch klappt meist schon besser.

Wie immer gilt: "Übung macht den Meister" oder "Versuch macht klug".

Aufbau der regulären Ausdrücke bei VIM 5.3

Die regulären Ausdrücke, kurz Muster genannt, beziehen sich bei VIM 5.3 immer auf die Zeichen einer Zeile und sind wie folgt definiert:
  1. Ein Muster (pattern) besteht aus mindestens einem Zweig (branch), wobei einzelne Zweige durch die Zeichenfolge \| zu trennen sind. Dieses Muster beschreibt die Vereinigung der Mengen der Zeichenketten, die durch die einzelnen Zweige angegeben werden. Das Trennzeichen ist auch als logisches Oder interpretierbar.

    Beispiel:

    abc\|def umfasst sowohl abc als auch def.
  2. Ein einzelner Zweig besteht aus mindestens einer Gruppe (piece). Mehrere Gruppen werden bei Bedarf einfach ohne Trennzeichen aneinandergereiht. Ein Zweig beschreibt alle Zeichenketten, die sich aus der Verkettung der durch die einzelnen Gruppen spezifizierten Teilzeichenketten ergeben.

    Beispiel:

    abc[0-9]def umfasst die Zeichenketten, die mit abc beginnen, dann eine Ziffer (0..9) folgen lassen und mit def enden.
  3. Eine Gruppe besteht aus einem Atom (atom), dem einige spezielle Zeichen bzw. Zeichenpaare folgen können, die angeben, wie oft die durch das Atom beschriebene Teilzeichenkette auftreten darf. Dabei ist zwischen gesetzter und rückgesetzter Option magic zu unterscheiden.

    Spezielle Zeichen hinter einem Atom:

    magic nomagic Anzahl des Auftretens des Atoms
    * \* 0 bis unendlich, so groß wie möglich
    \+ \+ 1 bis unendlich, so groß wie möglich
    \= \= 0 oder 1
    \? \? 0 oder 1
    \{n,m} \{n,m} n bis m, so groß wie möglich
    \{n} \{n} n
    \{n,} \{n,} n bis unendlich, so groß wie möglich
    \{,m} \{,m} 0 bis m, so groß wie möglich
    \{} \{} 0 bis unendlich, so groß wie möglich (äquivalent zu *)
    \{-n,m} \{-n,m} n bis m, so klein wie möglich
    \{-n} \{-n} n
    \{-n,} \{-n,} n bis unendlich, so klein wie möglich
    \{-,m} \{-,m} 0 bis m, so klein wie möglich
    \{-} \{-} 0 bis unendlich, so klein wie möglich

    Beispiele:

    magic nomagic dadurch beschrieben
    .* .\* ganze Zeile, auch wenn leer
    .\+ .\+ jede nichtleere Zeichenkette
    guu\=t guu\=t gut oder guut
  4. Hier eine Auswahl verfügbarer Atome:

Anmerkungen:

Beispiele:

^beep( beep( am Zeilenanfang
[a-zA-Z]$ jeder Buchstabe am Zeilenende
hier \(vorn\|hinten\) sind Zeile, in der zwischen den Wörtern hier und sind eines der Wörter vorn oder hinten steht, jeweils durch Leerzeichen getrennt

Suchen und Ersetzen mit VIM

Die Such-Kommandos / und ? erwarten generell als Argument einen regulären Ausdruck der oben beschriebenen Art. Eine gute Übersicht ist in der Online-Hilfe enthalten.

Beim Ersetzungs-Kommando :substitute folgt hinter dem Argument, das die zu ersetzenden Zeichenketten durch ein Such-Muster, d.h. durch einen regulären Ausdruck beschreibt, ein weiteres Argument, das die Ersatz-Zeichenkette angibt. Diese kann Rückbezüge auf Teile der zu ersetzenden Zeichenkette enthalten. Der konkrete Wert eines solchen Rückbezugs ist nicht statisch, sondern hängt jeweils von der aktuell unter Verwendung des Such-Musters gefundenen Zeichenkette ab.

Einige Zeichen der Ersatz-Zeichenkette haben daher eine Sonderbedeutung. Sofern es sich um Ctrl-Zeichen handelt, sind diese mit vorangestelltem Ctrl-V einzugeben. Ggf. kann die Sonderbedeutung durch Voranstellen eines Backslash aufgehoben werden.

magic nomagic Bedeutung
& \& Ersatz durch die komplette über das Such-Muster gefundene (und daher zu ersetzende) Zeichenkette
\& & Ersatz durch &
\0 Ersatz durch die komplette gefundene Zeichenkette
\1 Ersatz durch die dem ersten Sub-Ausdruck entsprechende Zeichenkette
\2 Ersatz durch die dem zweiten Sub-Ausdruck entsprechende Zeichenkette
... ...
\9 Ersatz durch die dem neunten Sub-Ausdruck entsprechende Zeichenkette
~ \~ Ersatz durch die Ersatz-Zeichenkette der vorigen Substitution
\~ ~ Ersatz durch ~ (Tilde)
\u Ersatz durch das groß geschriebene Folgezeichen
\U Ersatz durch die groß geschriebenen Folgezeichen
\l Ersatz durch das klein geschriebene Folgezeichen
\L Ersatz durch die klein geschriebenen Folgezeichen
\e Beendigung von \U, \L, \u und \l
\E dieselbe Wirkung wie \e
Ctrl-M Zeilenumbruch an dieser Stelle
\r wirkt wie Ctrl-M (Zeilenumbruch)
Ctrl-V Ctrl-M symbolisiert das Zeichen CR (Carriage Return)
\n symbolisiert das Zeichen NUL (Zeichen mit Dezimalwert 0)
\b symbolisiert das Zeichen BS (Backspace)
\t symbolisiert das Zeichen HT (Horizontal-Tabulator)

Beispiele:

Nachfolgend werden einige Ersetzungen gezeigt, die alle das Kommandos :s nutzen. Dessen genaue Syntax kann der Online-Hilfe entnommen werden.

:s/a\|b/xxx\0xxx/g ersetzt in der aktuellen Zeile alle Zeichen a gegen xxxaxxx und alle Zeichen b gegen xxxbxxx
:s/\([abc]\)\([efg]\)/\2\1/g kehrt in der aktuellen Zeile die Reihenfolge der Zeichen aller Zeichenpaare um, die mit einem der drei Zeichen abc beginnen und mit einem der drei Zeichen efg enden
:10s/abcde/abc^Mde/ fügt in Zeile 10 beim ersten Auftreten der Zeichenfolge abcde zwischen c und d eine Zeilenschaltung ein
:.,$s/$/\r/ fügt im Bereich von der aktuellen Zeile bis zum Dateiende an jede Zeile eine weitere Zeilenschaltung an
:%s/^M//g löscht alle Carriage-Return-Zeichen im Text
:s/.*/& &/ fügt an den bisherigen Inhalt der aktuellen Zeile ein Leerzeichen und anschließend den bisherigen Inhalt noch einmal an

So wird z.B. aus der alten Zeile abc die neue Zeile abc abc.

:s/\<[a-z]\+\>/\U&\e!/ wandelt alle Zeichen des ersten nur aus Kleinbuchstaben bestehenden Wortes der aktuellen Zeile in Großbuchstaben um und hängt ein ! an

Anmerkung: Sofern die Option ignorecase gesetzt ist, erfolgt keine Beachtung der Groß- und Kleinschreibung. D.h., es wird das erste nur aus Buchstaben (Klein- und Großbuchstaben) bestehende Wort der aktuellen Zeile in angegebener Weise verändert.

Auch bei den Kommandos :g und :v lassen sich die zu bearbeitenden Zeilen mittels regulärer Ausdrücke spezifizieren:

:g/^$/d löscht alle leeren Zeilen, d.h. diejenigen, die lediglich aus der Zeilenendekennzeichnung bestehen
:g/^(/s/a/b/g in allen Zeilen, die mit einer runden öffnenden Klammer beginnen, werden alle Buchstaben a gegen b ersetzt
:v/z$/d löscht alle Zeilen, die nicht auf z enden

Holger Trapp

letzte Modifikation: 25.10.2006