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 bzw. 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 daß 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äßt 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, daß 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 4.6 zur Verfügung stehenden regulären Ausdrücke sei darauf hingewiesen, daß 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 :-). Aber das kann ja alte Computer-Freaks nicht schocken. 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 Malheur. Nach einem kühnen Undo ist ja der alte Zustand wieder da, und ein neuer Versuch klappt bestimmt schon besser.

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

Aufbau der regulären Ausdrücke bei VIM 4.6

Die regulären Ausdrücke, kurz Muster genannt, beziehen sich bei VIM 4.6 immer auf die Zeichen einer Zeile und sind wie folgt definiert:
  1. Ein Muster besteht aus mindestens einem Zweig, 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 umfaßt sowohl abc als auch def.

  2. Ein einzelner Zweig besteht aus mindestens einer Gruppe. 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 umfaßt die Zeichenketten, die mit abc beginnen, dann eine Ziffer (0..9) folgen lassen und mit def enden.

  3. Eine Gruppe besteht aus einem 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 ... unendlich
    \+ \+ 1 ... unendlich (nicht bei Vi)
    \= \= 0 oder 1 Mal (nicht bei Vi)

    Beispiel:

    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 verschiedenen Suchbefehle, z.B. / oder ?, erwarten generell als Argument einen regulären Ausdruck der oben beschriebenen Art. Eine gute Übersicht ist in der Online-Hilfe enthalten.

Bei den Ersetzungskommandos folgt hinter dem Argument, das die zu ersetzende Zeichenkette beschreibt, ein weiteres Argument, das die Ersatz-Zeichenkette angibt, wobei Rückbezüge auf Teile der gefundenen, zu ersetzenden Zeichenkette möglich sind.

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 gefundene Zeichenkette
\& & Ersatz durch &
\0 Ersatz durch die gefundene Zeichenkette
\1 Ersatz durch die dem ersten Teilmuster entsprechende Zeichenkette
\2 Ersatz durch die dem zweiten Teilmuster entsprechende Zeichenkette
.. ..
\9 Ersatz durch die dem neunten Teilmuster 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 Ctrl-M
\n symbolisiert das Zeichen <NUL>
\b symbolisiert das Zeichen <BS>
\t symbolisiert das Zeichen <Tab>

Beispiele:

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

:s/a\|b/xxx\0xxx/g ersetzt in der aktuellen Zeile a gegen xxxaxxx und b gegen xxxbxxx
:s/\([abc]\)\([efg]\)/\2\1/g kehrt in der aktuellen Zeile die Reihenfolge der Zeichen aller Zeichenpaare um, die mit a, b oder c beginnen und mit e, f oder g 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/.*/& &/ dupliziert den Inhalt der aktuellen Zeile mit Leerzeichen als Trenner
: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 manipuliert.

Auch bei den Kommandos :g und :v lassen sich die zu bearbeitenden Zeilen mittels regülä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, 26. Januar 1997, 26. Juni 1998