zurück zum Artikel

"The Regular Expression"-Bibliothek

Rainer Grimm

Wann bietet es sich an, regulĂ€re AusdrĂŒcke zu verwenden? Ein paar typische AnwendungsfĂ€lle dokumentieren das.

Mein ursprĂŒnglicher Plan war es, ĂŒber die Regeln der C++ Core Guidelines zu der regex- und chrono-Bibliothek zu schreiben, aber abgesehen von der Überschrift gibt es keinen Inhalt. Heute werde ich die LĂŒcke schließen und ĂŒber die regex-Bibliothek schreiben.

Da ich bereits einige Artikel zur Zeitbibliothek [1] geschrieben habe, werde ich diese ignorieren. Jetzt geht es los mit meinen Regeln zu regulĂ€ren AusdrĂŒcken.

RegulĂ€re AusdrĂŒcke sind eine mĂ€chtige aber auch teure und komplizierte Maschinerie, um mit Text zu arbeiten. Wenn das Interface eines std::string oder ein Algorithmus der Standard Template Library den Job erledigen kann, ziehe ihn vor.

Wann sollst du regulĂ€re AusdrĂŒcke verwenden? Hier sind typische AnwendungsfĂ€lle:

Nur zur Klarstellung: Die Operationen wirken auf Zeichenmustern und nicht auf Text.

Zuerst einmal solltest du Raw Strings einsetzen, wenn du regulĂ€re AusdrĂŒcke definierst.

Der Einfachheit halber werde ich die vorherige Regel brechen. Der regulĂ€re Ausdruck fĂŒr den Text C++ ist ziemlich hĂ€sslich: C\\+\\+. Du musst zwei Backslashes fĂŒr jedes "+"-Zeichen verwenden. Den ersten Backslash, da das "+"-Zeichen ein spezielles Zeichen in einem regulĂ€ren Ausdruck ist. Den zweiten, da der Backslash ein besonderes Zeichen in einem String ist. Daher maskiert der eine Backslash ein "+"-Zeichen, der andere Backslash maskiert den Backslash.

Das folgende Beispiel mag dich vielleicht noch nicht ĂŒberzeugen:

std::string regExpr("C\\+\\+");
std::string regExprRaw(R"(C\+\+)");

Beide Strings stehen fĂŒr regulĂ€re AusdrĂŒcke, die dem Text C++ entsprechen. Insbesondere der Raw String R"(C\+\+) ist ziemlich hĂ€sslich zu lesen. R"(Raw String)" begrenzt den Raw String. Nebenbei gesagt, regulĂ€re AusdrĂŒcke und Pfadnamen und Windows ("C:\temp\newFile.txt") sind typische Kandidaten fĂŒr Raw Strings.

Stelle dir vor, du möchtest eine Fließkommazahl in einem Text finden, die du durch folgende Sequenz von Zeichen identifizierst: Tabulatur Fließkommazahl Tabulator \\DELIMITER. Hier ist ein konkretes Beispiel fĂŒr dieses Pattern: "\t5.5\t\\DELIMITER".

Das folgende Programm wendet einen regulÀren Ausdruck an, der in einem String und in einem Raw String enkodiert ist, um das Zeichenmuster zu finden:

// regexSearchFloatingPoint.cpp

#include <regex>
#include <iostream>
#include <string>

int main(){

std::cout << std::endl;

std::string text = "A text with floating pointer number \t5.5\t\\DELIMITER and more text.";
std::cout << text << std::endl;

std::cout << std::endl;

std::regex rgx("\\t[0-9]+\\.[0-9]+\\t\\\\DELIMITER"); // (1)
std::regex rgxRaw(R"(\t[0-9]+\.[0-9]+\t\\DELIMITER)"); // (2)

if (std::regex_search(text, rgx)) std::cout << "found with rgx" << std::endl;
if (std::regex_search(text, rgxRaw)) std::cout << "found with rgxRaw" << std::endl;

std::cout << std::endl;

}

Der regulĂ€re Ausdruck rgx("\\t[0-9]+\\.[0-9]+\\t\\\\DELIMITER") ist schwer zu lesen. Um n aufeinanderfolgende "\"-Symbole (Zeile 1) zu finden, musst du 2 * n "\"-Symbole verwenden. Im Gegensatz erlaubt die Verwendung eines Raw String, den regulĂ€ren Ausdruck genau so zu definieren wie das Zeichenmuster, das du suchst: rgxRaw(R"(\t[0-9]+\.[0-9]+\t\\DELIMITER)") (Zeile 2). Der Teilausdruck [0-9]+\.[0-9]+ steht fĂŒr eine Fließkommazahl. Zumindest eine Zahl ([0-9]+), gefolgt von einem Punkt (\.), gefolgt von mindestens einer Zahl ([0-9]+).

Der VollstÀndigkeit halber ist hier die Ausgabe des Programms:

The RegulÀr Expresson Bibliothek

Ehrlich gesagt, war das Beispiel recht einfach.

Die Anwendung von regulĂ€ren AusdrĂŒcken besteht typischerweise aus drei Schritten. Dies gilt fĂŒr std::regex_search und std::regex_match.

  1. ErklÀre den regulÀren Ausdruck.
  2. Speichere das Ergebnis der Suche.
  3. Analysiere das Ergebnis.

Was heißt das? Dieses Mal möchte ich die erste E-Mail Adresse in einem Text finden. Der folgende regulĂ€re Ausdruck (RFC 5322 Official Standard) findet nicht alle E-Mail Adressen, da diese sehr irregulĂ€r sind.

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[az0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x2\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

Der Lesbarkeit wegen, habe ich einen Zeilenumbruch in den regulĂ€ren Ausdruck eingefĂŒgt. Die erste Zeile entspricht dem lokalen Anteil der E-Mail Adresse und die zweite Zeile dem Domainanteil der E-Mail-Adresse. Mein Programm wird einen einfachen regulĂ€ren Ausdruck verwenden, um die E-Mail-Adresse zu finden. Der regulĂ€re Ausdruck ist nicht perfekt, tut aber seinen Job. ZusĂ€tzlich möchte ich den lokalen Anteil und den DomĂ€nenanteil der E-Mail-Adresse finden.

Hier geht es los:

// regexSearchEmail.cpp

#include <regex>
#include <iostream>
#include <string>

int main(){

std::cout << std::endl;

std::string emailText = "A text with an email address: rainer@grimm-jaud.de.";

// (1)
std::string regExprStr(R"(([\w.%+-]+)@([\w.-]+\.[a-zA-Z]{2,4}))");
std::regex rgx(regExprStr);

// (2)
std::smatch smatch;

if (std::regex_search(emailText, smatch, rgx)){

// (3)

std::cout << "Text: " << emailText << std::endl;
std::cout << std::endl;
std::cout << "Before the email address: " << smatch.prefix() << std::endl;
std::cout << "After the email address: " << smatch.suffix() << std::endl;
std::cout << std::endl;
std::cout << "Length of email adress: " << smatch.length() << std::endl;
std::cout << std::endl;
std::cout << "Email address: " << smatch[0] << std::endl; // (6)
std::cout << "Local part: " << smatch[1] << std::endl; // (4)
std::cout << "Domain name: " << smatch[2] << std::endl; // (5)

}

std::cout << std::endl;

}

Die Zeilen 1, 2 und 3 stehen fĂŒr die drei typischen Schritte fĂŒr regulĂ€re AusdrĂŒcke. Der regulĂ€re Ausdruck in Zeile 1 benötigt ein paar Worte.

Hier ist er: ([\w.%+-]+)@([\w.-]+\.[a-zA-Z]{2,4})

Die Ausgabe des Programms zeigt die genaue Analyse:

The RegulÀr Expresson Bibliothek

Ich bin noch nicht fertig. Es gibt noch mehr ĂŒber regulĂ€re AusdrĂŒcke in meinen nĂ€chsten Artikel zu schreiben. Ich werde ĂŒber die verschiedenen Arten von Text schreiben und darĂŒber, wie es sich leicht ĂŒber alle Treffer iterieren lĂ€sst.


( [2])

URL dieses Artikels:
https://www.heise.de/-4469602

Links in diesem Artikel:
[1] https://www.grimm-jaud.de/index.php/blog/tag/time
[2] mailto:rainer@grimm-jaud.de