Open Source im professionellen Einsatz
Linux-Magazin 10/2013

Modernes C++ in der Praxis – Folge 12

Suchen und ersetzen

Eine Logdatei anonymisieren, Fließkommazahlen oder Datumsangaben lokalisieren: Das alles lässt sich mit der C++11-Funktion std::regex_replace() in einem Rutsch umsetzen.

1013

Der Sprachstandard C++11 hat endlich reguläre Ausdrücke eingeführt. Wie moderner C++-Code damit Texte gegen Muster prüft oder Passagen findet, zeigte die vorige Folge dieser Artikelreihe [1]. Daneben kann der Programmierer die leistungsstarken Ausdrücke aber auch nutzen, um Strings zu verändern.

Logdatei anonymisieren

Als einfaches Beispiel soll ein Webserver-Log dienen, in dem die aufrufenden IP-Adressen anonymisiert werden sollen. Dabei kommt die Funktion »std::regex_replace()« zum Einsatz, die die verräterischen Zahlen durch »XXX.XXX.XXX.XXX« ersetzt. Sie benötigt in ihrer einfachsten Form drei Dinge, um ihre Arbeit zu erledigen: Der Aufruf

regex_replace(log,rgxIPs,"XXX.XXX.XXX.XXX")

enthält den String »log« , den regulären Ausdruck »rgxIPS« , der die zu ersetzende Zeichenmuster im Text identifiziert, sowie die Zeichenkette »XXX.XXX.XXX.XXX« , die die Stelle der identifizierten Zeichenmuster einnehmen soll. Dabei tauscht die Funktion »std::regex_replace()« alle Vorkommen des Zeichenmusters im Text aus und gibt den neuen String als Ergebnis zurück (Listing 1).

Listing 1

Anonymisieren einer Logdatei

01 // replace #include <boost/tr1/regex.hpp> with
02 //         #include <regex>
03 // and
04 // std::tr1:: with std:: in the source code
05
06 #include <boost/tr1/regex.hpp>
07
08 #include <iostream>
09 #include <string>
10
11 int main(){
12
13   std::cout << std::endl;
14
15   std::string log="66.249.64.13 - - [18/Sep/2012:11:07:48 +1000] GET /robots.txt HTTP/1.0 200 468 - Googlebot/2.1\n"
16                   "66.249.64.13 - - [18/Sep/2012:11:07:48 +1000] GET / HTTP/1.0 200 6433 - Googlebot/2.1";
17
18   std::cout << log << "\n\n";
19
20   // anonymize IPs
21   std::tr1::regex rgxIPs(R"([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})");
22
23   std::string anonymizeLog{std::tr1::regex_replace(log,rgxIPs,"XXX.XXX.XXX.XXX")};
24   std::cout <<  anonymizeLog << std::endl;
25
26   std::cout << std::endl;
27
28 }

Das Programm beginnt in bekannter Manier mit den Anpassungen an die Boost-Bibliothek [2], da weder Clang- noch GCC-Compiler die Bibliothek für reguläre Ausdrücke derzeit vollständig unterstützen. Der »log« -String in den Zeilen 15 und 16 enthält die zu anonymisierenden Daten. Anonymisieren bedeutet in diesem konkreten Fall, die IP-Adressen zu verschleiern. Dazu kommt der reguläre Ausdruck in Zeile 21 zum Einsatz: »rgxIPs« beschreibt vier Zahlen, die jeweils durch einen Punkt getrennt sind. Dabei kann jede der Zahlen eine bis drei Stellen aus den Ziffern von 0 bis 9 enthalten: »[0-9]{1,3}« .

Der Gesamtstring erhält die Form eines Raw-String (mit »R"« markiert), damit der Programmierer den Schrägstrich nicht zu schützen braucht. Zeile 23 vollzieht die Ersetzung und damit das Anonymisieren der Daten. Das Ergebnis landet im String »anonymizeLog« .

Zugegeben, das war ein besonders einfacher Anwendungsfall für »std::regex_replace()« . Bestand doch seine Aufgabe lediglich darin, die erkannten Textmuster zu überschreiben. Oft sind aber zwei Schritte beim Modifizieren eines Textes notwendig: Der erste identifiziert das Textmuster, der zweite modifiziert einen Teil des gefundenen Textes. Dank der so genannten Erfassungsgruppen stellt das keine große Herausforderung für »std::regex_replace()« dar.

Fließkommazahlen lokalisieren

Ein typisches Einsatzgebiet für Erfassungsgruppen ist das (Um-)Formatieren von Daten. Im Beispiel liegen die Fließkommazahlen des String »germanDoubles{"+0,85 -13,2 1,0 ,45 -13,7 1,03425 10134,25"}« in deutscher Lokalisierung vor. Um die Kommata in jeder Zahl des String durch die im Englischen üblichen Punkte zu ersetzen, kommt der reguläre Ausdruck in Abbildung 1 zum Einsatz. Dieser zerlegt jede Zahl in ihre zwei Komponenten vor und nach dem Komma. Das tut er mittels zweier Erfassungsgruppen, die Teile des Ausdrucks in runde Klammern einschließen.

Abbildung 1: Der Ausdruck für deutsche Fließkommazahlen verwendet zwei Erfassungsgruppen.

Die Erfassungsgruppen beeinflussen nicht, wie ein regulärer Ausdruck seine Treffer findet, sondern erlauben es, einzelne Teile eines Treffers explizit zu adressieren. So lassen sich die Vor- und Nachkommastellen einer Fließkommazahl, durch einen Punkt verbunden, wieder zusammensetzen – ein Format, das der englischen Lokalisierung der Zahlen entspricht. Listing 2 setzt die skizzierten Ideen in konkreten C++-Code um.

Listing 2

Fließkommazahlen formatieren

01 // replace #include <boost/tr1/regex.hpp> with
02 //         #include <regex>
03 // and
04 // std::tr1:: with std:: in the source code
05
06 #include <boost/tr1/regex.hpp>
07
08 #include <iostream>
09 #include <string>
10
11 int main(){
12
13   std::cout << std::endl;
14
15   std::string germanDoubles{"+0,85 -13,2 1,0 ,45 -13,7 1,03425 10134,25"};
16   std::cout << germanDoubles << std::endl;
17
18   // replace "," with "."
19   std::tr1::regex rgxDouble(R"(([-+]?[0-9]*),?([0-9]+))");
20
21   std::string englishDoubles{std::tr1::regex_replace(germanDoubles,rgxDouble,"$1.$2")};
22   std::cout << englishDoubles << std::endl;
23
24   std::cout << std::endl;
25
26 }

Zwei Stellen in Listing 2 verdienen größere Aufmerksamkeit. Das ist zum einen in Zeile 19 der reguläre Ausdruck »([-+]?[0-9]*),?([0-9]+)« , zum anderen die Anwendung der Erfassungsgruppen in »regex_replace(germanDoubles,rgxDouble,"$1.$2")« (Zeile 21). Zuerst zum regulären Ausdruck. Er besteht aus einem Teil vor und einem nach dem Komma. Dabei ist das Komma optional »,?« . Vor dem Komma kann ein Minus- oder Plus-Zeichen stehen, auf das beliebig viele Ziffern von 0 bis 9 folgen »[0-9]*« .

Während die Komponenten des regulären Ausdrucks vor dem Komma optional sind, sind die nach dem Komma obligatorisch. Der Nachkomma-Bestandteil der Fließkommazahl besteht ebenfalls aus den Ziffern von 0 bis 9 und muss mindestens eine Stelle aufweisen.

Den Vorkomma- und Nachkomma-Anteil der Fließkommazahl adressiert je eine der beiden Erfassungsgruppen, die sich anschließend in der Replace-Funktion verwenden lassen. Die Funktion »regex_replace()« ersetzt jedes Textmuster in dem Text »germanDoubles« durch den String »$1.$2« . Dabei stehen »$1« und »$2« für die erste und die zweite Erfassungsgruppe des identifizierten Textmusters. Abbildung 2 zeigt die Ausgabe des kleinen Programms.

Abbildung 2: Mit regulären Ausdrücken kann C++ das Format von Fließkommazahlen lokalisieren.

Erfassungsgruppen in regulären Ausdrücken sind ein leicht verständliches Konzept, das dennoch interessante Anwendungsfälle abdeckt. So lässt sich mit ihnen beispielsweise die Reihenfolge von Gruppen in einem zu ersetzenden String vertauschen.

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

  • C++11

    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.

  • C++11

    Der Prüfstein für reguläre Ausdrücke heißt Word Count. Das Zählen von Wörtern in einem Dokument zeigt, ob sich eine Programmiersprache für das Prozessieren von Text eignet. Der Autor des Artikels zeigt, dass sich der Klassiker in C++11 fast mit der Kompaktheit von Python implementieren lässt.

  • 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.

  • Logfiles

    In den Logdateien eines Linux-Systems und seiner Dienste rauscht vorüber, was auf dem Rechner passiert. Tools wie Logcheck und Logsurfer filtern die wichtigsten Ereignisse für den Admin heraus – oder leiten sogar automatisch die passende Reaktion ein.

  • Simple Regex Language

    Reguläre Ausdrücke helfen Admins und Entwicklern zwar in vielen Lebenslagen, entpuppen sich aber häufig auch als schwer verdaulich. Abhilfe will die Simple Regex Language schaffen. Mit ihr schreiben Entwickler den benötigten Ausdruck in natürlicher Sprache auf und vermeiden so nicht nur Tippfehler.

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.