Regular Expressions (zu deutsch: reguläre Ausdrücke) eignen sich hervorragend zur Mustererkennung (pattern matching).
Die C Standardbibliothek bietet die Möglichkeit nach Mustern zu suchen in einem einfachen String. Das Erstezen von Teilstrings ist nicht möglich.
Dieses Tutorial geht davon aus, dass Programmierkenntnisse und der Umgang mit der Programmiersprache C vorhanden sind.
Zuerst wenden wir uns einer einfachen Anwenung zu: dem pattern matching. Das Problem ist: es gibt einen String und dieser soll auf ein Muster geprüft werden. Hier interessiert nur, ob das Muster vorkommt oder nicht. Es sind keine weiteren Informationen von bedeutung (welche Teilstrings dem Muster ähneln).
#include <stdio.h>
#include <regex.h>
int match(char * string, char * pattern)
{
int result;
regex_t reg;
if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB) != 0) return -1;
result = regexec(®, string, 0, 0, 0);
regfree(®);
return result;
}
int main(int argc, char ** argv)
{
if (argc != 3)
{
fprintf(stderr, "error: wrong number of parameters\n");
return -1;
}
(match(argv[1], argv[2])) ? printf("NO_MATCH\n") : printf("MATCH\n");
return 0;
}
Zuerst ein paar header files:
Die Funktion match prüft den angegebenen String (string) auf das spezifizierte Muster (pattern). Wir benötigen zwei lokale Variablen. Eine, die einen Errorcode aufnehmen kann (result) und eine Struktur zur Prüfung des Strings (reg).
Zuerst müssen wir die Struktur reg initialisieren. Dies führen wir mit der Funktion regcomp
durch. Dieser nimmt einen Pointer auf die Struktur, das Pattern und Flags. Falls die Operation geglückt ist, liefert die Funktion 0. Falls nicht einen Fehlercode ungleicht 0.
Die eigentliche Operation geschieht nun mit Hilfe der Funktion regexec
. In dier hier benutzten Form, liefert sie bloss eine Erfolgsmeldung. Wurde das Muster gefunden, so wird 0 zurückgeliefert, sonst ein Fehlercode.
Zum Schluss müssen wir den Speicher der Struktur reg wieder freigeben. Dazu die Funktion regfree
. Als Rückgabewert unserer Funktion match liefern wir das Resultat von regexec.
Die Funktion main sollte dem erfahrenen C Programmierer keinerlei Schwierigkeiten bieten und wird deshalb nicht weiter erläutert.
Das Programm muss zuerst compiliert werden:
Danach kann es mit zwei Parametern gestartet werden:
Beispiel:
Oft ist es auch nützlich zu wissen wo das Muster überall gefunden wurde im String. Das obige Beispiel reicht hierfür nicht mehr, da dies lediglich auf ein generelles Vorkommen testet. Es gibt aber eine einfache und elegante Möglichkeit diese, evtl. mehreren, Vorkommnisse herauszuholen.
#include <stdio.h>
#include <regex.h>
void show(char * string, int start, int end)
{
int i;
printf("%d-%d: ", start, end);
for (i = start; i < end; ++i) putchar(string[i]);
putchar('\n');
}
void parts(char * string, char * pattern)
{
int absolute = 0;
int error;
regex_t reg;
regmatch_t pm;
if (regcomp(®, pattern, REG_EXTENDED) != 0) return;
error = regexec(®, string, 1, &pm, 0);
while (error == 0)
{
show(string, absolute+pm.rm_so, absolute+pm.rm_eo);
absolute += pm.rm_eo;
error = regexec(®, absolute+string, 1, &pm, REG_NOTBOL);
}
regfree(®);
}
int main(int argc, char ** argv)
{
if (argc != 3)
{
fprintf(stderr, "error: wrong number of parameters\n");
return -1;
}
parts(argv[1], argv[2]);
return 0;
}
Zunächst brauchen wir die gewohnten header files:
Wir definieren eine Funktion die es uns auf einfache Weise ermöglicht einen Teilstring und dessen Position anzuzeigen:
void show(char * string, int start, int end)
{
int i;
printf("%d-%d: ", start, end);
for (i = start; i < end; ++i) putchar(string[i]);
putchar('\n');
}
Der interessante Teil beginnt hier. Eine Funktion (parts) welche wiederum einen String und ein Muster übernimmt. Diesmal brauchen wir zwei Variablen mehr. Zum einen ist dies absolute, die die augenblickliche Position im String darstellt. Zum andern ist dies pm
vom Typ regmatch_t
. Diese brauchen wir um auf das Gefundene zu schliessen.
void parts(char * string, char * pattern)
{
int absolute = 0;
int error;
regex_t reg;
regmatch_t pm;
Die Compilation des Musters geschieht auf die gleiche Weise wie oben.
Das Finden des Musters geschieht nun mit zusätzlichen Parametern. Falls das Muster gefunden wurde, steht nun die Positionsinformation in der Struktur pm. Solange nun kein Fehler auftritt und das Muster weiterhin vorhanden ist, versuchen wir im verbleibenden String das Muster erneut zu finden. Zu beachten gibt’s noch, dass wenn wir die Variable string bei jedem Durchgang erhöhen würden, dann könnten wir sogar auf die Variable absolute verzichten. Bei jedem Durchgang wird dann der gefundene Teilstring und dessen Position ausgegeben.
error = regexec(®, string, 1, &pm, 0);
while (error == 0)
{
show(string, absolute+pm.rm_so, absolute+pm.rm_eo);
absolute += pm.rm_eo;
error = regexec(®, absolute+string, 1, &pm, REG_NOTBOL);
}
regfree(®);
}
Auf weitere Ausführungen der Funktion main wird an dieser Stelle verzichtet.
Nach Hinweis und auf Wunsch von Florian Guist eine kurze Erklärung zur Zeile 25: bei dem Ausdruck absolute+string
handelt es sich um die aboslute Position innerhalb des Strings an welchem der nächste regex gesucht werden soll. Man könnte natürlich (als Einstiegshilfe in die Pointer-Arithmetik) auch schreiben: &(string[absolute])
(und nein: Pointer-Arithmetik ist nicht generell schlecht).
Das Programm muss zuerst compiliert werden:
Danach kann es mit zwei Parametern gestartet werden:
Beispiel:
Alle source code Files die auf dieser Seite publiziert werden, sind frei zu kopieren, modifizieren, weiterverbreiten und zu verwenden. Das copyright des Tutorials hat Mario Konrad.
REG_EXTENDED
: Benutzt erweiterte regular expressionsREG_ICASE
: Ignoriert Gross-/KleinschreibungREG_NOSUB
: %%regexec%% soll nur Erfolg oder Fehler zurückliefern, keine PositionsangabenREG_NEWLINE
: Wenn dieses Flag nicht gesetzt ist, dann wird das newline Zeichen als normales Zeichen behandelt. Falls das Flag gesetzt ist, dann wird das newline Zeichen auch als normales Zeichen behandelt mit folgenden Ausnahmen:
.
im Muster, ausserhalb von Klammern und bei jeder Form einer Ungleichheitsliste.^
im Muster, dazu verwendet den Anfang der Zeile zu spezifieren, gleicht einem Leerstring unmittelbar nach dem newline Zeichen, ohne Berücksichtigung der Einstellung von REG_NOTBOL
.$
im Muster, dazu verwendet das Ende der Zeile zu spezifizieren, gleicht einem Leerstring unmittelbar vor dem newline Zeichen, ohne Berücksichtigung der Einstellung von REG_NOTEOL
.REG_NOTBOL
: Das erste Zeichen des Strings ist nicht der Anfang des eigentlichen Strings. Das ^
hat also keine Bedeutung mehr für den Zeilenanfang.REG_NOTEOL
: Das letzte Zeichen des Strings ist nicht das Ende des eigentlichen Strings. Das $
hat also keine Bedeutung mehr für das Zeilenende.REG_BADPAT
: Das angebebene Muster ist fehlerhaftREG_ECOLLATE
REG_ECTYPE
: Ein ungültiger Zeichenklassentyp wurde referenziertREG_EESCAPE
: Ein abschliessendes \
ist im MusterREG_ESUBREG
: Eine Ziffer in \digit
ist fehlerhaftREG_EBRACK
: Es existiert eine [ ]
UngleichheitREG_ENOSYS
: Die Funktion ist nicht unterstütztREG_EPAREN
: Es existiert eine \( \)
oder ( )
UngleichheitREG_EBRACE
: Es existiert eine { }
UngleichheitREG_BADBR
: Der Inhalt zwischen { }
ist fehlerhaftREG_ERANGE
: Ein Endpunkt in einem Bereichsausdruck ist nicht erlaubtREG_ESPACE
: Nicht genügend Speicher verfügbarREG_BADRPT
: ?
, *
oder +
wurde nicht von einem gültigen Ausdruck angeführtDie wichtigsten Referenzen sind die man-pages der folgenden Funktionen:
regcomp(3)
regexec(3)
regfree(3)
regerror(3)