Die Boost C++ Bibliotheken

Kapitel 9. Boost.Xpressive

Boost.Xpressive bietet so wie Boost.Regex Funktionen an, um Strings mit regulären Ausdrücken zu durchsuchen. Reguläre Ausdrücke müssen jedoch nicht als String angegeben werden. Boost.Xpressive ermöglicht es, reguläre Ausdrücke als C++-Code zu schreiben. Das hat den Vorteil, dass bereits zur Kompilierung festgestellt werden kann, ob ein regulärer Ausdruck gültig ist oder sich zum Beispiel ein Tippfehler eingeschlichen hat.

Beachten Sie, dass ausschließlich Boost.Regex in C++11 Einzug gehalten hat. Die Standardbibliothek bietet keine Unterstützung, um reguläre Ausdrücke als C++-Code angeben zu können.

Boost.Xpressive bietet mit boost/xpressive/xpressive.hpp eine Headerdatei an, die Sie einbinden können, um Zugriff auf die meisten Funktionen der Bibliothek zu erhalten. Für einige Funktionen müssen Sie weitere Headerdateien einbinden. Alle Definitionen der Bibliothek befinden sich im Namensraum boost::xpressive.

Beispiel 9.1. Strings mit boost::xpressive::regex_match vergleichen
#include <boost/xpressive/xpressive.hpp>
#include <string>
#include <iostream>

using namespace boost::xpressive;

int main()
{
  std::string s = "Boost Libraries";
  sregex expr = sregex::compile("\\w+\\s\\w+");
  std::cout << std::boolalpha << regex_match(s, expr) << '\n';
}

Boost.Xpressive bietet grundsätzlich die gleichen Funktionen wie Boost.Regex. Sie befinden sich lediglich im Namensraum von Boost.Xpressive: boost::xpressive::regex_match() vergleicht Strings, boost::xpressive::regex_search() sucht in Strings, und boost::xpressive::regex_replace() ersetzt Zeichen in Strings. Beispiel 9.1, in dem die Funktion boost::xpressive::regex_match() Anwendung findet, ähnelt daher Beispiel 8.1.

Beachten Sie, dass es einen wesentlichen Unterschied zwischen Boost.Xpressive und Boost.Regex gibt. Der Typ des regulären Ausdrucks in Boost.Xpressive richtet sich nach dem Typ des Strings, der durchsucht werden soll. Da der String s im Beispiel 9.1 den Typ std::string hat, muss der reguläre Ausdruck den Typ boost::xpressive::sregex erhalten. Sehen Sie sich Beispiel 9.2 an, in dem der reguläre Ausdruck auf eine Zeichenkette vom Typ const char* angewandt wird.

Beispiel 9.2. boost::xpressive::cregex bei Zeichenketten vom Typ const char*
#include <boost/xpressive/xpressive.hpp>
#include <iostream>

using namespace boost::xpressive;

int main()
{
  const char *c = "Boost Libraries";
  cregex expr = cregex::compile("\\w+\\s\\w+");
  std::cout << std::boolalpha << regex_match(c, expr) << '\n';
}

Für Zeichenketten vom Typ const char* verwenden Sie die Klasse boost::xpressive::cregex. Verwenden Sie andere Typen wie beispielsweise std::wstring oder const wchar_t*, greifen Sie auf boost::xpressive::wsregex oder boost::xpressive::wcregex zu.

Beachten Sie, dass Sie für reguläre Ausdrücke, die Sie als String angeben, die statische Methode compile() aufrufen müssen – und zwar für den Typ, den Sie für den regulären Ausdruck verwenden.

Boost.Xpressive unterstützt eine direkte Initialisierung für reguläre Ausdrücke, die als C++-Code angegeben werden. Sie müssen den regulären Ausdruck in der für Boost.Xpressive eigenen Notation angeben. Sehen Sie sich dazu Beispiel 9.3 an.

Beispiel 9.3. Ein regulärer Ausdruck als C++-Code
#include <boost/xpressive/xpressive.hpp>
#include <string>
#include <iostream>

using namespace boost::xpressive;

int main()
{
  std::string s = "Boost Libraries";
  sregex expr = +_w >> _s >> +_w;
  std::cout << std::boolalpha << regex_match(s, expr) << '\n';
}

Der reguläre Ausdruck, der im Beispiel 9.2 als String \w+\s\w+ angegeben wurde, lautet nun +_w >> _s >> +_w. Es handelt sich dabei um den gleichen regulären Ausdruck. Auch im Beispiel 9.3 wird nach mindestens einem alphanumerischen Zeichen gesucht, gefolgt von genau einem Leerzeichen, wiederum gefolgt von mindestens einem alphanumerischen Zeichen.

Boost.Xpressive macht es möglich, reguläre Ausdrücke als C++-Code zu schreiben. Dazu bietet die Bibliothek Objekte für Zeichenklassen an. So ist zum Beispiel das Objekt _w gleichbedeutend mit \w. _s wiederum hat die gleiche Bedeutung wie \s.

Während bei regulären Ausdrücken als String Zeichenklassen wie \w und \s direkt hintereinander angegeben werden können, müssen Objekte wie _w und _s mit einem Operator verknüpft werden. Andernfalls würde es sich nicht um gültigen C++-Code handeln. Boost.Xpressive bietet den Operator operator>> an, der entsprechend im Beispiel 9.3 zum Einsatz kommt.

Um anzugeben, dass mindestens ein alphanumerisches Zeichen gefunden werden soll, wird _w ein Pluszeichen vorangestellt. Während die Syntax regulärer Ausdrücke vorsieht, dass eine Angabe zur Häufigkeit nach einer Zeichengruppe angegeben wird – also wie bei \w+ – muss das Pluszeichen vor _w angegeben werden. Beim Pluszeichen handelt es sich um einen unären Operator, der in C++ einem Objekt vorangestellt werden muss.

Boost.Xpressive ahmt die Regeln regulärer Ausdrücke nach, soweit sie in C++ nachgeahmt werden können. Die Bibliothek stößt dabei an Grenzen. So existiert zum Beispiel mit dem Fragezeichen ein Metazeichen, das in regulären Ausdrücken einmal oder keinmal bedeutet. Da das Fragezeichen kein gültiger Operator in C++ ist, ersetzt Boost.Xpressive es durch das Ausrufezeichen. Aus einer Angabe wie \w? wird bei Boost.Xpressive !_w, da das Ausrufezeichen vorangestellt werden muss.

Während es bei Funktionen wie regex_match() fraglich ist, ob Boost.Xpressive Boost.Regex vorgezogen werden sollte, unterstützt die Bibliothek Aktionen, die mit Teilausdrücken verknüpft werden können – etwas, was Boost.Regex nicht kennt. Sehen Sie sich dazu Beispiel 9.4 an.

Beispiel 9.4. Aktionen mit Teilausdrücken verknüpfen
#include <boost/xpressive/xpressive.hpp>
#include <boost/xpressive/regex_actions.hpp>
#include <string>
#include <iterator>
#include <iostream>

using namespace boost::xpressive;

int main()
{
  std::string s = "Boost Libraries";
  std::ostream_iterator<std::string> it{std::cout, "\n"};
  sregex expr = (+_w)[*boost::xpressive::ref(it) = _] >> _s >> +_w;
  std::cout << std::boolalpha << regex_match(s, expr) << '\n';
}

Wenn Sie Beispiel 9.4 ausführen, wird nicht mehr nur true für boost::xpressive::regex_match() ausgegeben. Es wird außerdem Boost ausgegeben.

Sie können Teilausdrücke mit Aktionen verknüpfen. Eine Aktion wird ausgeführt, wenn der entsprechende Teilausdruck gefunden wurde. So ist im Beispiel 9.4 für den Teilausdruck +_w die Aktion *boost::xpressive::ref(it) = _ angegeben. Es handelt sich dabei um eine Lambda-Funktion. Das Objekt _ bezieht sich auf die Zeichen, die mit dem Teilausdruck gefunden wurden – in diesem Fall das erste Wort in der Variablen s. Die entsprechenden Zeichen werden dem dereferenzierten Iterator it zugewiesen. Da es sich dabei um einen Iterator vom Typ std::ostream_iterator handelt, der mit std::cout initialisiert ist, wird Boost auf die Standardausgabe ausgegeben.

Beachten Sie, dass Sie die Funktion boost::xpressive::ref() verwenden müssen, um den Iterator it zu kapseln. Erst dadurch wird es möglich, _ dem dereferenzierten Iterator zuzuweisen. So handelt es sich bei _ um ein Objekt, das von Boost.Xpressive im Namensraum boost::xpressive angeboten wird und das normalerweise nicht einem dereferenzierten Iterator vom Typ std::ostream_iterator zugewiesen werden kann. Da die Zuweisung außerdem nicht sofort erfolgen darf, sondern erst dann, wenn Boost mit +_w gefunden wurde, verwandelt boost::xpressive::ref() die Zuweisung in eine Operation, die als lazy bezeichnet wird: Während der Code, der in den eckigen Klammern an +_w übergeben wird, gemäß den Regeln in C++ zuerst ausgeführt wird, kann die Zuweisung an den Iterator it erst dann stattfinden, wenn der reguläre Ausdruck angewandt wird. Die Angabe *boost::xpressive::ref(it) = _ stellt eine Zuweisung dar, die nicht sofort ausgeführt wird.

Beachten Sie, dass im Beispiel 9.4 die Headerdatei boost/xpressive/regex_actions.hpp eingebunden wurde. Dies ist notwendig, weil Aktionen nicht automatisch über boost/xpressive/xpressive.hpp zur Verfügung stehen.

Boost.Xpressive unterstützt wie Boost.Regex Iteratoren, um mit regulären Ausdrücken Strings zu zerlegen. Sie greifen dazu auf die Klassen boost::xpressive::regex_token_iterator oder boost::xpressive::regex_iterator zu. Sie können außerdem wie bei Boost.Regex Locale mit einem regulären Ausdruck verknüpfen, um einen anderen als den globalen Locale zu verwenden.