Open Source im professionellen Einsatz
Linux-Magazin 08/2013

Modernes C++ in der Praxis – Folge 11

Starke Ausdrücke

Was dem Perl-Programmierer altvertraut ist, fehlte C++ bisher. Der neue Sprachstandard C++11 bringt reguläre Ausdrücke ins Spiel, mit denen sich Textmuster beschreiben und finden lassen.

999

Reguläre Ausdrücke stellen eine Beschreibungssprache für Textmuster dar. C++11 verhilft C++-Entwicklern endlich zu dem lang vermissten Feature. Dieser Artikel führt mit einem kleinen Anwendungsbeispiel in das Programmieren mit Regular Expressions (Regex) ein: Ein E-Mail-Header enthält viele Adressen, die es herauszuklauben lohnt. Wer das nicht von Hand tun möchte, schreibt ein Programm, das diese Arbeit komfortabel und vor allem automatisch mit regulären Ausdrücken erledigt.

Sachter Anfang

Selbst die kompliziertesten Suchanforderungen lassen sich mittels regulärer Ausdrücke definieren. Zum Einstieg in des Thema verwendet dieser Artikel aber einen möglichst einfach gehaltenen Ausdruck, der eine E-Mail-Adresse beschreibt (Abbildung 1). Streng genommen müsste ein regulärer Ausdruck für eine Mailadresse dem RFC 2822 [1] folgen. Ein vollständig standardkonformer Ausdruck [2] ist allerdings sehr schwer verdaulich (siehe Abbildung 2).

Abbildung 1: Vereinfachter regulärer Ausdruck, der auf eine gültige E-Mail-Adresse passt. Er enthält Zeichen, Zeichenklassen und Klammern.

Abbildung 2: Der reguläre Ausdruck für E-Mail-Adressen ist in seiner standardkonformen Version kaum zu verstehen.

Wie ist der reguläre Ausdruck zu lesen? Am einfachsten gelingt dies mit dem »@« -Zeichen 1 als Ausgangspunkt. Es trennt den lokalen Teil vom Domänenteil einer E-Mail-Adresse. Der lokale Anteil darf verschiedene, durch eckige Klammern 2 zusammengehaltene Zeichen enthalten. Zum einen sind dies Wortzeichen. Sie umfassen Zeichen wie Buchstaben, Zahlen und Unterstriche, die in Wörtern auftreten können. Da diese Zeichen sehr häufig zur Anwendung kommen, sehen die regulären Ausdrücke eine eigene Zeichenklassen dafür vor: »\w« 3.

Weitere gültige Zeichen im lokalen Anteil einer E-Mail-Adresse sind der Punkt ».« , das Prozent- »%« , das Plus- »+« und das Minuszeichen »-« . Von all diesen Zeichen, also »\w.%+-« , muss der lokale Teil mindestens eines enthalten, was das den Klammern folgende Pluszeichen 4 besagt.

Nach dem »@« folgt der Domänenanteil. Dieser beginnt mit mindestens einem Zeichen aus der Menge »\w.-« gefolgt von einem Punkt ».« . Der Punkt ist ein besonderes Zeichen in regulären Ausdrücken, daher muss ihn der Programmierer in diesem Kontext durch einen Backslash 5 maskieren, um einen gewöhnlichen Punkt anzugeben.

Jetzt fehlt nur noch die Top-Level-Domain, und schon ist die E-Mail-Adresse beschrieben. Sie besteht aus einem Wort aus kleinen und großen Buchstaben 6, das nur zwei bis vier Zeichen lang sein darf 7. Zugegeben, dieser reguläre Ausdruck für eine E-Mail-Adresse besitzt noch ein paar Ungenauigkeiten, beispielsweise könnte der Domänenanteil einen Unterstrich enthalten, aber das würde der Einfachheit des Beispiels entgegenstehen.

Exakte Treffer

Die simpelste Frage, die sich mit einem regulären Ausdruck beantworten lässt, ist, ob eine Zeichenkette eine gültige E-Mail-Adresse darstellt. Diese Aufgabe erledigt Listing 1.

Listing 1

Exakte Treffer mit regex_match()

01 // replace #include <boost/tr1/regex.hpp>
02 //    with #include <regex>
03 #include <boost/tr1/regex.hpp>
04
05 #include <iostream>
06 #include <string>
07
08 // replace std::tr1:: with std:: in the source code
09 int main(){
10
11   std::cout << std::endl;
12
13   std::string email="rainer@grimm-jaud.de";
14
15   // regular expression for the email address
16   std::string regExprStr(R"(([\w.%+-]+)@([\w.-]+\.[a-zA-Z]{2,4}))");
17
18   // regular expression holder
19   std::tr1::regex rgx(regExprStr);
20
21   // search result holder
22   std::tr1::smatch smatch;
23
24   // looking for an exact match
25   if (std::tr1::regex_match(email,smatch,rgx)){
26
27     std::cout << "Text: " << email << std::endl;
28     std::cout << std::endl;
29     std::cout << "Email address: " << smatch[0] << std::endl;
30     std::cout << "Local part: " << smatch[1] << std::endl;
31     std::cout << "Domain name: " << smatch[2] << std::endl;
32
33   }
34
35   std::cout << std::endl;
36
37 }

Um den Sourcecode konform mit dem neuen C++-Standard zu formulieren, muss der Programmierer den standardkonformen Header (Zeile 3) und den Namensraum (Zeile 8) verwenden. Der vorige Artikel [3] in dieser Reihe erläutert ausführlich die Hintergründe. »rainer@grimm-jaud.de« ist die E-Mail-Adresse, die dem Muster »([\w.%+-]+)@([\w.-]+\.[a-zA-Z]{2,4})« entsprechen soll.

Ein scharfer Blick auf den String »regExprStr« im Programmcode erzeugt anfänglich ein wenig Verwirrung, beginnt er doch mit »R"(« . Das bedeutet, dass ein Raw-String folgt. Der wiederum bewirkt, dass C++11 eine Escape-Sequenz wie den Backslash »\« im String nicht interpretiert. Dadurch ist es nicht mehr notwendig, das Zeichen durch einen weitere Backslash zu schützen. Ein Raw-String besitzt in C++11 die Form »R"(Zeichenkette)"« . Für reguläre Ausdrücke sollte ihn der C++-Programmierer wann immer möglich verwenden.

Der reguläre Ausdruck »regExprStr« unterscheidet sich in zwei weiteren Punkten von dem regulären Ausdruck in Abbildung 1. In »regExprStr« ist sowohl der lokale Anteil als auch der Domänenteil durch zusätzliche runde Klammern gekapselt. Dank dieser weiteren Klammern stehen deren Inhalte als Erfassungsgruppen zu Verfügung. Dadurch kann der Code auf den lokalen Adressenteil mit »smatch[1]« und den Domänenteil mit »smatch[2]« explizit zugreifen. Dies ist schön in Abbildung 3 zu sehen.

Abbildung 3: Mit regex_match() findet der Programmierer den exakten Treffer, auf Wunsch in lokalen und Domänenteil getrennt.

Der Rest von Listing 1 ist schnell erklärt: Zeile 19 instanziert den regulären Ausdruck mit Hilfe von »regExprStr« . Zeile 25 fragt ab, ob der String »email« dem regulären Ausdruck »rgx« entspricht. Das Ergebnis dieser Abfrage speichert der Code in der Variablen »smatch« (Zeile 22). Fällt diese Antwort positiv aus, gibt Zeile 29 den gesamten Treffer mit »smatch[0]« aus. Das Match-Objekt »smatch« kann aber noch deutlich mehr als nur den Gesamttreffer und seine Erfassungsgruppen (Zeilen 30 und 31) anbieten.

Eine mächtigere Antwort verlangt aber eine anspruchsvollere Frage. Die Frage lautet daher nicht mehr, ob der eingegebene Text einer E-Mail-Adresse entspricht, sondern ob sich eine E-Mail-Adresse in einem Text finden lässt.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 4 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Reichhaltiges Angebot

    C++0x bringt nicht nur Veränderungen in der Kernsprache. Die Standardbibliothek der C++-Neuausgabe hat Multithreading, asynchrone Funktionsaufrufe, reguläre Ausdrücke und vieles mehr im Angebot.

  • C++11

    Container sind nicht nur bei der Virtualisierung derzeit in aller Munde, auch dem Programmierer sind Behälter für Objekte nützlich. Die Version 11 von C++ enthält einige Algorithmen, die die Arbeit mit solchen Containern deutlich vereinfachen.

  • C++11

    In C++11 lässt sich manches prägnanter formulieren als in klassischem C++. Dieser Artikel zeigt, wie die neue Range-basierte For-Schleife und die automatische Typableitung dabei helfen.

  • C++11

    Klein, aber oho: Platziert ein Entwickler drei Punkte ("…") geschickt an der richtigen Stelle im C++-Code, entpacken die so genannten Variadic Templates ihre Argumente an Ort und Stelle.

  • C++11

    Die neue Zeitbibliothek ist ein elementarer Baustein nicht nur für die Mulithreading-Fähigkeit von C++. Mit ihrer Hilfe legt der Entwickler einen Thread bis zu einem definierten Zeitpunkt schlafen oder fordert auf gut Glück ein Lock für eine Zeitspanne an.

comments powered by Disqus

Ausgabe 10/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.