Reguläre Ausdrücke

Martin Kompf

Reguläre Ausdrücke sind ein mächtiges Instrument, um Texte zu durchsuchen und zu formatieren. In Programmiersprachen wie Perl sind sie daher schon fest eingebaut; der C++ Programmierer kann Bibliotheken wie das frei verfügbare regex++ einsetzen.

Nach dem Download von regex++ müssen die erforderlichen Bibliotheken zunächst auf dem eigenen Computer übersetzt werden. Für Benutzer des Borland C++ Compilers/Builders, Microsoft Visual C++ 6.0 oder des GCC 2.95 sollte diese Prozedur relativ problemlos ablaufen.

Reguläre Ausdrücke sind Entwicklern, die in Perl programmieren oder die Unix-Tools awk, sed oder egrep verwenden, bereits in Fleisch und Blut übergegangen. Auch für den C-Programmierer gibt es seit langen die POSIX Funktionen regcomp und regexec (nur nicht für Microsoft Visual C++). Regex++ verwendet zur Angabe der regulären Ausdrücke genau die gleiche, POSIX konforme Syntax wie diese bekannten Tools. Jedoch besitzt regex++ ein komfortables C++ Interface, welches den Einsatz von regulären Ausdrücken im C++ Programm sehr einfach macht.

Die Funktion

#include <boost/regex.hpp>

bool check_car_number(const string s)
{
    static const boost::regex e("\\u{1,3}-\\u{1,2} \\d{1,4}[ \\0]*");
    return regex_match(s, e);
}

überprüft zum Beispiel durch Aufruf der Funktion regex_match, ob die im String s übergebene Zeichenkette ein in Deutschland gültiges Autokennzeichen ist. Der Schlüssel dazu ist der reguläre Ausdruck e. Hierin bedeutet die Sequenz \u{1,3} eine Folge von ein bis drei Großbuchstaben und \d{1,4} ein bis vier Ziffern. Andere Zeichen, wie - oder das Leerzeichen, werden literal (so wie sie sind) interpretiert. Da der reguläre Ausdruck als Zeichenkette in einem C++ Programm steht, müssen bestimmte Zeichen, die eine Sonderbedeutung für den C Compiler haben, mit dem Backslash \ maskiert werden. Der reguläre Ausdruck \u muss dann also mit \\u kodiert werden.

Das nächste Beispiel verwendet die Funktion regex_split, um alle Links aus einer HTML Datei zu extrahieren und auszugeben. Der reguläre Ausdruck e bildet dabei die Syntax des HTML Tags <a href="url"> ab, die Option boost::regbase::icase sorgt für eine von Groß- oder Kleinschreibung unabhängige Auswertung der Eingabe:

#include <list>
#include <fstream>
#include <iostream>
#include <boost/regex.hpp>

void load_file(std::string& s, std::istream& is)
{
   s.erase();
   s.reserve(is.rdbuf()->in_avail());
   char c;
   while(is.get(c))
   {
      if(s.capacity() == s.size())
         s.reserve(s.capacity() * 3);
      s.append(1, c);
   }
}

boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*"?([^" >]*)",
               boost::regbase::normal | boost::regbase::icase);


int main(int argc, char** argv)
{
   std::string s;
   std::list<std::string> l;

   for(int i = 1; i < argc; ++i)
   {
      std::cout << "Findings URL's in " << argv[i] << ":" << std::endl;
      std::ifstream is(argv[i]);
      l.clear();
      load_file(s, is);
      boost::regex_split(std::back_inserter(l), s, e);
      std::copy(l.begin(), l.end(),
          std::ostream_iterator<std::string>(std::cout, "\n"));
   }
   return 0;
}

Zum Suchen und Ersetzen von Text kann die Funktion regex_merge verwendet werden. Das folgende Beispielprogramm dient zur Umwandlung von HTML Dateien in reinen Text. Dazu werden im ersten Schritt sämtliche HTML Tags <...> durch eine leere Zeichenkette ersetzt, im zweiten Schritt erfolgt die Umwandlung der benannten Zeichen wie &lt; &gt; usw. in ihr entsprechendes ASCII-Äquivalent:

boost::regex tag_exp("<[^>]*>", boost::regbase::normal | boost::regbase::icase);
boost::regex amp_exp("(&lt;)|(&gt;)|(&amp;)|(&quot;)|(&laquo;)|(&raquo;)|"
                     "(&auml;)|(&Auml;)|(&ouml;)|(&Ouml;)|(&uuml;)|(&Uuml;)|"
                     "(&szlig;)|(&nbsp;)");
const char* amp_format = "(?1<)(?2>)(?3&)(?4")(?5")(?6")"
                         "(?7ä)(?8Ä)(?9ö)(?10Ö)(?11ü)(?12Ü)"
                         "(?13ß)(?14 )";


int main(int argc, char** argv)
{
   std::string s;

   for(int i = 1; i < argc; ++i)
   {
      std::cout << "Converting " << argv[i] << " to text:" << std::endl;
      std::ifstream is(argv[i]);
      load_file(s, is);
      s = boost::regex_merge( s, tag_exp, "");
      std::ostream_iterator<char> out(std::cout);
      boost::regex_merge( out, s.begin(), s.end(), amp_exp, amp_format);
   }
   return 0;
}

Dem Einsteiger in die Thematik der regulären Ausdrücke sei die Lektüre der regex++ Dokumentation (Online Version) und der Manual Pages regex (7) und perlre (1) empfohlen.