Boost::Xpressive - Альтернатива Boost::Regex

Не так давно я писал об использовании библиотеки Boost::Regex для регулярных выражений в C++. В этой заметке, я опять затрону эту тему, только теперь рассмотрю другой компонент Boost, предназначенный для обработки регулярных выражений: Boost::Xpresive.

Итак, во-первых, чем же он отличается от Boost::Regex:


  • Xpressive не требует линковки никаких дополнительных библиотек;

  • Утверждается, что Xpressive в среднем работает быстрее;

  • В Xpressive реглярные выражения могут описываться не только динамически (в виде строки, передаваемой в некую функцию), но и статически в виде C++ кода.

  • Регулярные выражения Xpressive могут ссылаться друг на друга, образуя целые грамматики
Для того, чтобы использовать библиотеку необходимо подключить один заголовочный файл. Библиотека находится в пространстве имен boost::xpressive.

#include <boost/xpressive/xpressive.hpp>

using namespace boost::xpressive;
Сразу стоит отметить проблему, которая может возникнуть при использовании библиотеки: Когда я попытался скомпилировать примеры для версии 1.34 компилятором GCC 4.3, то получил набор достаточно невнятных ошибок вида:

In file included from /usr/include/boost/xpressive/detail/dynamic/matchable.hpp:21,
                 from /usr/include/boost/xpressive/detail/core/linker.hpp:27,
                 from /usr/include/boost/xpressive/detail/core/icase.hpp:14,
                 from /usr/include/boost/xpressive/regex_primitives.hpp:17,
...
.../core/state.hpp:34: error: changes meaning of ‘match_context’ from ‘struct boost::xpressive::detail::match_context<BidiIter>’
...
In file included from /usr/include/boost/xpressive/detail/static/productions/charset_transforms.hpp:18,
                 from /usr/include/boost/xpressive/detail/static/productions/complement_compiler.hpp:19,
...
.../chset/basic_chset.hpp:28: error: changes meaning of ‘basic_chset’ from ‘struct boost::xpressive::detail::basic_chset<typename Traits::char_type>’
Причина оказалась в несовместимости версии 1.34 и GCC 4.3. В результате, примеры я собирал с помощью GCC 4.2. В 4.1 все тоже работает. Других проблем при работе с блиотекой у меня не возникло.

Динамические регулярные выражения

Динамические регулярные выражения описываются и используются практически также, как и в Boost::Regex. Здесь я опишу только отличия:
  • Регулярное выражение из строки создается не конструктором, а с помощью специальной функции sregex::compile
  • Отсутствуют некоторые функции класса basic_regex: те, которые позволяют рассматривать выражение как строку, и те, которые относятся к компиляции выражения (они перенесены в класс regex_compiler)
Более полный список отличий можно найти в соответствующем разделе документации к библиотеке. На практике, скорее всего, придется столкнуться только с первым ограничением. Итак, учитывая его пример с проверкой e-mail, приведенный в статье про Boost::Regex выглядит следующим образом:

#include <boost/xpressive/xpressive.hpp>

#include <string>
#include <iostream>

using namespace boost::xpressive;

bool can_be_email( const std::string & s ) {
        static const sregex e = sregex::compile( "[a-zA-Z0-9_\\.]+@([a-zA-Z0-9]+\\.)+[a-zA-Z]{2,4}" );
        return regex_match( s, e );
}
Как можно увидеть, все изменения - заголовочные файлы, пространства имен и использование sregex::compile.

Статические регулярные выражения

Особенностью данной библиотеки является возможность описывать статические регулярные выражения, которые описываются прямо в C++ коде. Этот подход имеет некоторые преимущества:
  • Проверка синтаксиса регулярных выражений на этапе компиляци;
  • Возможность использовния данных и кода C++, в том числе других регулярных выражений;
  • Статический код лучше встраивается и оптимизируется;
  • В принципе, возможно определение регулярных выржений нне только над строками, но и, например, над массивами чисел.
Для того, чтобы статически определить регулярное выражение необходимо присвоить переменной типа basic_regex<>(sregex, cgerex и т.п.) значение некоторого выражения, записываемого следующим образом:

sregex re1 = '$' >> +_d >> '.' >> _d >> _d;
sregex re2 = +( _d | _w );
То есть по сути, записью регулярного выражения в виде C++ кода. При этом, допустимы следующие конструкции:
  • _ - произвольный символ (аналог .)
  • a >> b - последовательность (аналог ab)
  • a | b - альтернатива
  • (s1= a) - группировка (аналог (a))
  • *a,+a,!a - проивзольное число повторений a, больше 0, 0 или 1,не a (a*,a+,a?)
  • repeat<n,m>(a) - от n до m повторений a (a{n,m})
  • _w - буква (\w)
  • _d - цифра (\d)
  • _s - пробельный символ (\s)
Полный список можно найти здесь. Теперь, попробуем описать тот же самый пример (e-mail) в виде статического регулярного выражения:

#include <boost/xpressive/xpressive.hpp>

#include <string>
#include <iostream>

using namespace boost::xpressive;

bool can_be_email( const std::string & s ) {
	// static const sregex e = sregex::compile( "[a-zA-Z0-9_\\.]+@([a-zA-Z0-9]+\\.)+[a-zA-Z]{2,4}" );
        static const sregex e = +(_w|_d|'.') >> '@' >> +( +(_w|_d) >> '.' ) >> repeat<2,4>(_w);
        return regex_match( s, e );
}
Таким образом, регулярное выражение достаточно просто переписывается в C++ нотации. P.S. Если Вам в принципе нравится идея описания в C++ коде грамматик, то стоит посмотреть также в сторону библиотеки Boost::Spirit, которая предназначена для описания парсеров в очень сходной нотации. В документации приведены таблицы сравнения производительности Boost::Xpressive со статическими и динамическими регулярными выражениями и Boost::Regex.

Использование уже описанных регулярных выражений

Одной из интересных возможностей статических регулярных выражений является использования уже описанных выше выражений. Например, наше регулярное выражения для проверки e-mail явно содержит в себе подвыражение для проверки имени домена. Значит, его можно выделить в отдельную сущность и затем использовать повторно! С использованием Boost::Xpressive это делается совсем просто:

bool can_be_email( const std::string & s ) {
        static const sregex name = +(_w|_d|'.'); // Вот это вот регулярное выражение на имя ящика
        static const sregex domain = +( +(_w|_d) >> '.' ) >> repeat<2,4>(_w); // А это на домен
        static const sregex email = name >> '@' >> domain; // А вместе - на e-mail

        return regex_match( s, email );
}
Окромя вышеописанных, Boost::Xpressive содержит еще уйму возможностей, таких как таблицы символов, семантические действия и т.п. К сожалению, здесь я их описывать не буду (может быть что-нибудь в другой заметке =) ), так что интересующимся предлагаю почитать документацию к библиотеке.

 Подписаться на RSS

 #  #  #  #  #  #  #  #  #  #

Добавить комментарий