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)
#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++, в том числе других регулярных выражений;
- Статический код лучше встраивается и оптимизируется;
- В принципе, возможно определение регулярных выржений нне только над строками, но и, например, над массивами чисел.
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)
#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 содержит еще уйму возможностей, таких как таблицы символов, семантические действия и т.п. К сожалению, здесь я их описывать не буду (может быть что-нибудь в другой заметке =) ), так что интересующимся предлагаю почитать документацию к библиотеке.
