Pull to refresh

Boost это просто. Часть 1. Boost.Regex

C++ *
Эта статья является первой в цикле статей, которые я собираюсь посвятить, наверное лучшей, библиотеке для С++.
В данной статье рассматриваются следующие вопросы касательно регулярных выражений:
  • regex_match
  • regex_search
  • regex_replace
  • regex_iterator
  • regex_token_iterator
  • Partial match


Введение


Я не хочу вступать в полемику по поводу нужности или не нужности регулярных выражений, каждый для себя решает сам. Моей целью было донести простоту использования Boost.Regex для тех, кому нравится использовать регулярные выражения. Для тех кому регулярные выражения не знакомы я советую прочесть хотя бы Википедию, а если кто-то хочет поглубже с ними познакомится, то я бы посоветовал Mastering regular expressions.
Boost.Regex является собираемой библиотекой, т.е для ее использования необходимо ее собрать. Как это сделать написано в Getting started.
Собирая библиотек вы можете выбрать один из двух алгоритмов, которые будет использоваться в движке регулирных выражений: рекурсивный и не-рекурсивный. Первый быстрый, но может грозить переполнением стека, второй немного медленней, но безопасный. Макросы для определения разных способов BOOST_REGEX_RECURSIVE и BOOST_REGEX_NON_RECURSIVE соответственно. Так же, каждый алгоритм может быть немного настроен. Макросы для настройки и их описание можно посмотреть Здесь
Boost.Regex поддерживает следующие типы синтаксисов для регулярных выражений:
  1. Perl(по умолчанию)
  2. POSIX Extended
  3. POSIX Basic

Необходимо учесть, что '.'(точка) по умолчанию включает в себя '\n'. Это может быть изменено передачей специального флага, в соответствующий алгоритм.

Основные алгоритмы


boost::regex_match


Данный алгоритм используется для проверки соответсвия входящей строки и некоторого регулярного выражения, возвращая true Если строка соответсвует и false в другом случае.
Типичный способ использования: regex_match(входящая_строка, [результаты_нахождения_соответствий], регулярное_выражение, [флаги]).
Полный список всех перегруженных объявлений смотри в документации.
Пример его использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w+)-(\\d+)-(\\w+)-(\\d+)");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::cout << "Does this line match our needs? " << std::boolalpha << boost::regex_match(xStr,  xResults, xRegEx) << "\n";
std::cout << "Print entire match:\n " << xResults[0] << std::endl;
std::cout << "Print the former string into another format:\n" << xResults[1] << "+"
                                << xResults[2] << "+"
                                << xResults[3] << "+"
                                << xResults[4] << std::endl;


* This source code was highlighted with Source Code Highlighter.

Результатом работы будет:
==========================Results==============================
Does this line match our needs? true
Print entire match:
AAAA-12222-BBBBB-44455
Print the former string into another format:
AAAA+12222+BBBBB+44455

Небольшое отступление от алгоритма, для описания его параметров. Эти параметры используются во всех алгоритмах, но рассмотрим их только здесь.


результаты_нахождения_соответствий — является опциональным параметром и есть ни что иное, как объект класса match_results. Этот объект является массивом объектов класса sub_match, который в свою очередь, не более чем объект-хранитель итераторов на начало и конец найденного соответсвия в строке. результаты_нахождения_соответствий служит для сохранения результатов работы алгоритма. Так, если алгоритм был выполнен успешно, то нулевой член массива будет хранить sub_match для всего найденного соответствия(исключение состовляет использование partial match, но об этом позже). Каждый последующий член массива будет хранить итераторы на каждый capture содержащийся в регулярном выражении. Каждый элемент массива может быть проверен на наличие контента через флаг matched. Важно помнить, что каждый sub_match хранит итераторы на входящую_строку, поэтому нельзя передавать в качестве исходной строки временный объект и в дальнейшем использовать результаты алгоритма, в лучшем случае получите assert в дебаге, в худшем undefined behavior с головной болью. При рекурсивном capture в регулярном выражении(например "(\w)+")) в результирующий match_result попадет только последний capture, это поведение по умолчание, которое можно изменить. Чтобы мы могли получить доступ ко всем рекурсивным capture, мы должны передать флаг match_extra в [флаги], но это еще не все, для того, чтобы match_extra сработал, необходим объявить дефайн BOOST_REGEX_MATCH_EXTRA во всех транслируемых юнитах. Или просто раскомментировать define в boost/regex/user.hpp. Это функциональность помечена как эксперементальная и сильно уменьшающая производительность. У меня так и не получилось ее опробовать, т.к моя VS2008 выдает Access violation в недрах xutulity при попытке использования алгоритмов regex_* с раскомментированным дефайном. Не протестированный пример ее использования:
std::string xStr("The boost library has a great opportunity for the regex!");
boost::regex xRegEx("(\\b\\w{5}\\b)*");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";  
if( boost::regex_search(xStr, xResults, xRegEx, boost::match_extra) )
{
  std::cout << "Words consist from exact 5 digits have been found in our line:\n";
  for(int j = 0; j < xResults.captures(1).size(); ++j)
     std::cout << xResults.captures(1)[j] << std::endl;
}


* This source code was highlighted with Source Code Highlighter.

[Флаги] — необязательный параметр, с дефолтовым значением match_default. Про доступные флаги, можно посмотреть здесь. Флаги комбинируются посредством '|'(or).

Partial match


Частичное соответсвие необходимо для проверки входной строки, на частичное соответсвие регулярному выражению. Это может быть полезным при валидации поступающих асинхронно данных или при больших объемах данных, т.е в тех случаях когда в конкретный момент времени нет возможности провести полное соответсвие между регулярным выражением и исходной строкой. Чтобы использовать partial match, необходимо передать флаг match_partial в [флаги]. При этом, если используется частичное соответствие, то используемый алгоритм(regex_match, regex_search etc.) вернет true, но флаг matched у нулевого элеменат match_results будет уставновлен в false. То, что было найдено в результате частичного соответствия, можно получить через этот же нулевой элемент.
Пример использования:
std::string xStr("AAAA-12222");
boost::regex xRegEx("(\\w+)-(\\d+)-(\\w+)-(\\d+)");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::cout << "Does this line match the regex? " << std::boolalpha << boost::regex_match(xStr, xResults, xRegEx,
                 boost::match_default | boost::match_partial) << "\n";
std::cout << "Is it the partial match? " << std::boolalpha <<  !xResults[0].matched << "\nPrint the partial match:\n" << xResults[0] << std::endl;


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Does this line match the regex? true
Is it the partial match? true
Print the partial match:
AAAA-12222

regex_search


Данный алгоритм предназначен для поиска подстроки в исходной строке, по заданному регулярному выражению.
Формат использования выглядит следующим образом:
regex_search(входящая_строка, [результаты_нахождения_соответствий], регулярное_выражение, [флаги]).
Пример использования:
std::string xStr("The boost library has a great opportunity for the regex!");
boost::regex xRegEx("\\b(?:\\w+?)((\\w)\\2)(?:\\w+?)\\b");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::string::const_iterator xItStart = xStr.begin();
std::string::const_iterator xItEnd = xStr.end();
while( boost::regex_search(xItStart, xItEnd, xResults, xRegEx) )
{
  std::cout << "Word, we've searched, is \"" << xResults[0] << "\". It has two \"" << xResults[2] << "\" inside itself.\n";
  xItStart = xResults[1].second;
}


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Word, we've searched, is «boost». It has two «o» inside itself.
Word, we've searched, is «opportunity». It has two «p» inside itself.


regex_replace


Алгоритм используется для замены всех вхождений подстрок, соответсвующих регулярному выражению, на строку заданному в определенном формате. Результат может быть получен через итератор, переданный в качестве аргумента либо как возращаемая строка. Части сроки которые не соответствуют регулярному выражению, копируются в выходнуб строку не измененными, если не задан флаг format_no_copy, который оставляет только заматченные строки в результате. При переданном флаге format_first_only, заменяется только первая подстрока, соответствующая регулярному выражению.
Типично используемый формат:
regex_replace(входящая_строка, регулярное_выражение, форматная_строка, [флаги]).
форматная_строка определяет строку, на которую будет заменятся найденная подстрока.
Она может подчинятся одному из следующих правил синтаксиса:
  • sed флаг:format_sed
  • Perl(по умолчанию) флаг:format_perl
  • Boost-extended флаг:format_all
  • Литеральный, т.е не использует никаких специальных символов. Флаг:format_literal

Пример использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w+)-(\\d+)-(\\w+)-(\\d+)");
std::string xFormatString("$1*$2*$3*$4");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::cout << "Print string after replace:\n " << boost::regex_replace(xStr, xRegEx, xFormatString, boost::match_default | boost::format_perl) << std::endl;
  


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Print string after replace:
AAAA*12222*BBBBB*44455


Вспомогательные средства


regex_iterator


Данный итератор может быть удобен для последовательного поиска вхождений подстроки, соответствующей регулярному выражению. При каждом инкрементировании находится следующая подстрока, с помощью regex_search. При разыменовывании итератора мы получаем объект типа match_results, с помощью которого мы можем получить всю необходимую информацию.
Формат использования: regex_iterator(начальный_итератор, конечный _итератор, регулярное_выражение)
Пример использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w|\\d)+");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
boost::sregex_iterator xIt(xStr.begin(), xStr.end(), xRegEx);
boost::sregex_iterator xInvalidIt;
while(xIt != xInvalidIt)
  std::cout << *xIt++ << "*";


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
AAAA*12222*BBBBB*44455*


regex_token_iterator


Очень полезный интрумент для разбиаения строки на токены,
Формат использования: regex_token_iterator(начальный_итератор, конечный _итератор, регулярное_выражение, [submatch])
[submatch] используется для указания, как следует интерпретировать токены в строке.
При -1 итератор возвращает часть последовательности, которая не соответствует регулярному выражению. Т.е возвращается либо строка, которая идет после первого совпадения, до начала следующего совпадения(не включая первый символ совпадения). Либо, с начала строки, если начала строки не удовлетворяет регулярному выражению. Т.е при передаче -1, регулярное выражения является разделителем. При 0, каждое смещение итератора(++) дает следующую часть строки которая была “заматчена“, т.е каждый разыменованный итератор является capture строки. При любом положительном числе, в качестве параметра, выбирается capture регулярного выражения соответствующий числу, переданному в качестве параметра. Так же можно передать массив индексов в качестве параметра, тогда итератор будет искать каждый capture согласно индексам в массиве, т.е если массив состоит из {4, 2, 1}, тогда начальный итератор будет указывать на 4 capture, следующий итератор на 2 и т.д. Процесс будет повторятся для всей последовательности, пока не закончатся соответствия для данного регулярного выражения. По дефолту это параметр равен 0.
Разыменованный итератор является объектом класса sub_match.
Примеры использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w|\\d)+");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
boost::sregex_token_iterator xItFull(xStr.begin(), xStr.end(), xRegEx, 0);
boost::sregex_token_iterator xInvalidIt;
std::cout << "Result the same as the regex_iterator: \n";
while(xItFull != xInvalidIt)
  std::cout << *xItFull++ << "*";
//Parts of captures
boost::regex xRegEx2("(\\w+)-(\\d+)");
boost::sregex_token_iterator xItFirstCapture(xStr.begin(), xStr.end(), xRegEx2, 1);
std::cout << "\nShow only first captures: \n";
while(xItFirstCapture != xInvalidIt)
  std::cout << *xItFirstCapture++ << "*";
//Reverse order
int aIndices[] = {2,1};
boost::sregex_token_iterator xItReverseCapture(xStr.begin(), xStr.end(), xRegEx2, aIndices);
std::cout << "\nShow captures in the reverse order: \n";
  while(xItReverseCapture != xInvalidIt)
std::cout << *xItReverseCapture++ << "*";
//Delimiters
boost::regex xRegEx3("(\\w|\\d)+");
boost::sregex_token_iterator xItDelimiters(xStr.begin(), xStr.end(), xRegEx3, -1);
std::cout << "\nShow delimiters: \n";
while(xItDelimiters != xInvalidIt)
  std::cout << *xItDelimiters++ << " ";


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Result the same as the regex_iterator:
AAAA*12222*BBBBB*44455*
Show only first captures:
AAAA*BBBBB*
Show captures in the reverse order:
12222*AAAA*44455*BBBBB*
Show delimiters:
— — -

Замечание


Любой алгоритм может выбросить исключение типа std::runtime_error в случае если сложность проверки полного соответствия(matching) N элементов начнет превышать О(N^2) или в случае переполнения стека(если Boost.Regex был собран в рекурсивном режиме)

Tags:
Hubs:
Total votes 60: ↑57 and ↓3 +54
Views 73K
Comments Comments 39