![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
User's GuideBoost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 43. Boost.Xpressive
|
![]() |
Note |
---|---|
Большинство остальных примеров в этом документе оставят без внимания директиву< |
Далее вы заметите тип объекта регулярного выражения<sregex
>. Если вы знакомы сBoost.Regex, это отличается от того, к чему вы привыкли. «<s
>» в «<sregex
>» означает «<string
>», что указывает на то, что этот регекс может использоваться для нахождения паттернов в<std::string
>объектах. Я подробно обсудю эту разницу и ее последствия позже.
Обратите внимание, как инициализируется объект regex:
sregex rex = sregex::compile( "(\\w+) (\\w+)!" );
Для создания объекта регулярного выражения из струны необходимо назвать фабричный метод, такой как<
. Это еще одна область, в которой xpressive отличается от других объектно-ориентированных библиотек регулярных выражений. Другие библиотеки поощряют вас думать о регулярном выражении как о своеобразной веревке на стероидах. В депрессивном состоянии регулярные выражения не являются струнами; они представляют собой небольшие программы на доменном языке. Стринги являются лишь однимпредставлениемэтого языка. Другим представлением является шаблон выражения. Например, приведенная выше строка кода эквивалентна следующему:basic_regex<>::compile()
>
sregex rex = (s1= +_w) >> ' ' >> (s2= +_w) >> '!';
Это описывает то же самое регулярное выражение, за исключением того, что оно использует доменно-специфический встроенный язык, определяемый статическим xpressive.
Как видите, статические регексы имеют синтаксис, заметно отличающийся от стандартного синтаксиса Perl. Это связано с тем, что мы ограничены синтаксисом C++. Самое большое различие заключается в том, что<>>
>означает «последующий». Например, в Perl можно просто поставить суб-выражения рядом друг с другом:
abc
Но в C++ должен быть оператор, разделяющий подвыражения:
a >> b >> c
В Перле скобки<()
>имеют особое значение. Они группируются, но как побочный эффект они также создают обратные ссылки, такие как<$1
>и<$2
>. В C++ нет способа перегрузить скобки, чтобы дать им побочные эффекты. Чтобы получить тот же эффект, мы используем специальные<s1
>,<s2
>и т. Д. Токены. Назначьте один, чтобы создать обратную ссылку (известную как подматч в xpressive).
Вы также заметите, что оператор повторения<+
>переместился из позиции постфикса в позицию префикса. Это потому, что у C++ нет оператора постфикса<+
>. Итак:
"\\w+"
Это то же самое, что:
+_w
Мы рассмотрим все остальные различияпозже.
Есть два способа стать депрессивным. Первое и самое простое — скачать последнюю версию Boost. Просто перейдите наhttp://sf.net/projects/boostи перейдите по ссылке& #8220;Download& #8221;.
Второй способ — прямой доступ к репозиторию Boost Subversion. Просто перейдите наhttp://svn.boost.org/trac/boost/и следовать инструкциям для анонимного доступа к Subversion. Версия в Boost Subversion нестабильна.
Xpressive - это библиотека шаблонов только для заголовков, что означает, что вам не нужно изменять скрипты сборки или ссылаться на какой-либо отдельный файл lib, чтобы использовать его. Все, что вам нужно сделать, это<#include<boost/xpressive/xpressive.hpp>
>. Если вы используете только статические регексы, вы можете улучшить время компиляции, только включив<xpressive_static.hpp
>. Вы также можете включить<xpressive_dynamic.hpp
>, если вы планируете использовать только динамические регексы.
Если вы также хотите использовать семантические действия или пользовательские утверждения со своими статическими регексами, вам необходимо дополнительно включить<regex_actions.hpp
>.
Для Xpressive требуется версия 1.34.1 или выше.
В настоящее время, Boost. Известно, что Xpressive работает на следующих компиляторах:
Ознакомьтесь с последними результатами тестов на странице результатов регрессии Boost.
![]() |
Note |
---|---|
Пожалуйста, отправьте любые вопросы, комментарии и отчеты об ошибках в eric |
Вам не нужно много знать, чтобы начать продуктивно работать с депрессией. Начнем с никелевого тура по типам и алгоритмам, которые предоставляет xpressive.
Table 43.1. xpressive's Tool-Box
Инструмент |
Описание |
---|---|
Contains a compiled regular expression. |
|
< |
|
Проверяет, соответствует ли строка регексу. Чтобы< |
|
Searches a string to find a sub-string that matches the regex.
|
|
Given an input string, a regex, and a substitution string, |
|
An STL-compatible iterator that makes it easy to find all the places
in a string that match a regex. Dereferencing a |
|
Like |
|
A factory for |
Теперь, когда вы немного знаете об инструментах, вы можете выбрать правильный инструмент для вас, ответив на следующие два вопроса:
Большинство классов в xpressive - это шаблоны, которые параметризованы по типу итератора. Xpressive определяет некоторые общие типдефы, чтобы облегчить выбор правильных типов. Вы можете использовать таблицу ниже, чтобы найти правильные типы в зависимости от типа вашего итератора.
Table 43.2. xpressive Typedefs vs. Iterator Types
std::string::const_iterator |
char const * |
std::wstring::const_iterator |
char_t const * |
|
---|---|---|---|---|
|
< |
< |
< |
|
< |
|
|
|
|
|
|
< |
< |
|
< |
< |
|
|
|
< |
< |
< |
|
Вы должны заметить, что систематическая конвенция именования. Многие из этих типов используются вместе, поэтому соглашение об именах помогает вам использовать их последовательно. Например, если у вас есть<sregex
>, вы также должны использовать<smatch
>.
Если вы не используете один из этих четырех типов итераторов, вы можете использовать шаблоны напрямую и указать тип итератора.
Вы хотите найти шаблон один раз? Много раз? Поиск и замена? У хладнокровия есть инструменты для всего этого и многого другого. Ниже приведена краткая ссылка:
Table 43.3. Tasks and Tools
Чтобы сделать это... |
Используйте это... |
---|---|
The |
|
|
< |
Алгоритм< |
|
|
Класс< |
The |
|
The |
Эти алгоритмы и классы подробно описаны в справочном разделе.
![]() |
Tip |
---|---|
Попробуйте нажать на задачу в таблице выше, чтобы увидеть полную примерную программу, которая использует xpressive для решения этой конкретной задачи. |
Первое, что вы сделаете при использовании xpressive, это создадите объект<
. В этом разделе рассматриваются гайки и болты построения регулярного выражения на двух диалектах: статичной и динамичной.basic_regex<>
>
Особенностью, которая действительно отличает xpressive от других библиотек регулярных выражений C/C++, является возможность создания регулярных выражений с использованием выражений C++. xpressive достигает этого за счет перегрузки оператора, используя метод, называемыйшаблонами выражения, для встраивания мини-языка, посвященного сопоставлению шаблонов в C++. Эти «статические регексы» имеют много преимуществ перед своими струнными собратьями. В частности, статические регексы:
Поскольку мы составляем статические регексы с использованием выражений C++, мы ограничены правилами для легальных выражений C++. К сожалению, это означает, что «классический» синтаксис регулярных выражений не всегда может быть четко отображен на C++. Скорее, мы сопоставляем регекс, выбирая новый синтаксис, который является законным C++.
Вы создаете статический регекс, назначая его объекту типа<
. Например, следующее определяет регекс, который может быть использован для поиска закономерностей в объектах типа<basic_regex<>
>std::string
>:
sregex re = '$' >> +_d >> '.' >> _d >> _d;
Назначение работает аналогично.
В статических регексах буквалы символов и струн совпадают сами с собой. Например, в приведенном выше регексе<'$'
>и<'.'
>соответствуют символам<'$'
>и<'.'
>соответственно. Не следует путать с тем, что<$
>и<.
>являются мета-характерами в Perl. В прессинге буквалы всегда представляют себя.
При использовании букв в статических регексах необходимо позаботиться о том, чтобы хотя бы один операнд не был буквальным. Например, следующиенедействительные регексы:
sregex re1 = 'a' >> 'b'; // ERROR! sregex re2 = +'a'; // ERROR!
Два операнда двоичного оператора<>>
>являются буквальными, а операнд унарного оператора<+
>также является буквальным, поэтому эти утверждения будут называть нативный двоичный оператор правого смещения C++ и унарный плюс операторы соответственно. Это не то, чего мы хотим. Чтобы вызвать перегрузку оператора, по крайней мере один операнд должен быть типом, определенным пользователем. Мы можем использовать функцию помощника<as_xpr()
>Xpressive, чтобы «запятнать» выражение с регексностью, заставляя оператора перегружать, чтобы найти правильных операторов. Два вышеуказанных правила должны быть записаны как:
sregex re1 = as_xpr('a') >> 'b'; // OK sregex re2 = +as_xpr('a'); // OK
Как вы, наверное, уже заметили, суб-выражения в статических регексах должны быть разделены оператором секвенирования<>>
>. Вы можете прочитать этого оператора как «следующий».
// Match an 'a' followed by a digit sregex re = 'a' >> _d;
Альтернатива работает так же, как в Perl с оператором<|
>. Вы можете прочитать этого оператора как «или». Например:
// match a digit character or a word character one or more times sregex re = +( _d | _w );
В Perl скобки<()
>имеют особое значение. Они группируются, но как побочный эффект они также создают обратные ссылки, такие как<$1
>и<$2
>. В C++ скобки только групповые — нет способа дать им побочные эффекты. Чтобы получить тот же эффект, мы используем специальные токены<s1
>,<s2
>и т. Д. Присвоение одному создает обратную ссылку. Затем вы можете использовать обратную ссылку в своем выражении, например, используя<\1
>и<\2
>в Perl. Например, рассмотрим следующий регекс, который находит соответствующие HTML-теги:
"<(\\w+)>.*?</\\1>"
В статической депрессии это будет:
'<' >> (s1= +_w) >> '>' >> -*_ >> "</" >> s1 >> '>'
Обратите внимание, как вы захватываете обратную ссылку, назначая<s1
>, а затем используете<s1
>позже в шаблоне, чтобы найти соответствующий конечный тег.
![]() |
Tip |
---|---|
Группировка без задней ссылки |
Perl позволяет вам сделать часть вашего обычного выражения «случай-нечувствительный», используя модификатор шаблона<(?i:)
>. Кроме того, у него есть модификатор чувствительности к случаю, называемый<icase
>. Вы можете использовать его следующим образом:
sregex re = "this" >> icase( "that" );
В этом регулярном выражении<"this"
>будет точно совпадать, но<"that"
>будет совпадать независимо от случая.
Регулярные выражения, не учитывающие конкретные случаи, поднимают вопрос о интернационализации: как следует оценивать сопоставления характеристик, не учитывающих конкретные случаи? Кроме того, многие классы персонажей локализованы. Какая из них соответствует<digit
>, а какая —<alpha
>? Ответ зависит от объекта<std::locale
>, который используется объектом регулярного выражения. По умолчанию все объекты регулярного выражения используют глобальную локализацию. Вы можете преодолеть по умолчанию, используя модификатор шаблона<imbue()
>, следующим образом:
std::locale my_locale = /* initialize a std::locale object */; sregex re = imbue( my_locale )( +alpha >> +digit );
Это регулярное выражение будет оценивать<alpha
>и<digit
>в соответствии с<my_locale
>. См. разделЛокализация и характеристики регексовдля получения дополнительной информации о том, как настроить поведение ваших регексов.
В приведенной ниже таблице перечислены знакомые конструкции регекса и их эквиваленты в статической депрессии.
Table 43.4. Perl syntax vs. Static xpressive syntax
Перл |
Статический xpressive |
значение |
---|---|---|
< |
< |
любой символ (при условии модификатора Perl/s). |
< |
< |
секвенирование< |
< |
< |
чередование< |
< |
< |
сгруппировать и получить обратную ссылку. |
< |
< |
группировать и не фиксировать обратную ссылку. |
< |
< |
ранее полученная обратная ссылка. |
< |
< |
ноль или более раз, жадный. |
< |
< |
Один или несколько раз, жадный. |
< |
< |
ноль или один раз, жадный. |
< |
< |
между< |
< |
< |
ноль или более раз, не жадный. |
< |
< |
один или несколько раз, не жадный. |
< |
< |
ноль или один раз, не жадный. |
< |
< |
между< |
< |
< |
начало утверждения последовательности. |
< |
< |
конец утверждения последовательности. |
< |
< |
Утверждение границы слова. |
< |
< |
не является утверждением границ. |
< |
< |
Буквальная новая линия. |
< |
< |
любой символ, кроме буквальной новизны (без модификатора Perl/s). |
< |
< |
Новая логическая линия. |
< |
< |
любой отдельный символ не является логической новой линией. |
< |
< |
символ слова, эквивалентный множеству [alnum | '_']. |
< |
< |
не является символом слова, эквивалентным ~set [alnum | '_']. |
< |
< |
Цифровой знак. |
< |
< |
не является цифрой. |
< |
< |
Пространственный характер. |
< |
< |
Не является космическим персонажем. |
< |
< |
— буквенно-цифровой знак. |
< |
< |
Азбука. |
< |
< |
Горизонтальный символ белого пространства. |
< |
< |
Управляющий персонаж. |
< |
< |
Цифровой знак. |
< |
< |
Графический персонаж. |
< |
< |
Нижний регистр. |
< |
< |
Печатный знак. |
< |
< |
— знак препинания. |
< |
< |
Белое пространство. |
< |
< |
Верхний регистр. |
< |
< |
шестнадцатеричная цифра. |
< |
< |
символы в диапазоне< |
< |
< |
символов< |
< |
< |
то же, что и выше |
< |
символов< |
|
< |
то же, что и выше |
|
< |
< |
не символы< |
< |
< |
матчвещине принимая во внимание случай. |
< |
< |
независимое подвыражение, совпадениематериалаи выключение обратного отсчета. |
< |
< |
позитивный взгляд вперед утверждение, матч если раньшевещи, но не включатьвещив матче. |
< |
< |
отрицательный взгляд вперед утверждение, совпадение если не раньшематериал. |
< |
< |
позитивный взгляд за утверждением, матч послевещи, но не включатьвещив матче.материалдолжен иметь постоянную ширину.] |
< |
< |
отрицательный взгляд за утверждением, совпадение если не послевещи.вещидолжны быть постоянной шириной.] |
< |
< |
Создать именованный захват. |
< |
< |
Возвращайтесь к ранее созданному названию захвата. |
Статические регексы денди, но иногда вам нужно что-то более динамичное. Представьте, что вы разрабатываете текстовый редактор с функцией поиска / замены регекса. Вы должны принять регулярное выражение от конечного пользователя в качестве ввода во время выполнения. Должен быть способ разобрать строку в обычное выражение. Для этого и нужны динамические регексы. Они построены из тех же основных компонентов, что и их статические аналоги, но они запаздывают, поэтому вы можете указать их во время выполнения.
Существует два способа создания динамического регекса: с функцией<
или с шаблоном класса<basic_regex<>::compile()
>
. Используйте<regex_compiler<>
>
, если вы хотите получить место по умолчанию. Используйте<basic_regex<>::compile()
>
, если вам нужно указать другую локализацию. В разделеграмматики регексамы увидим другое использование<regex_compiler<>
>
.regex_compiler<>
>
Вот пример использования<basic_regex<>::compile()
>:
sregex re = sregex::compile( "this|that", regex_constants::icase );
Вот тот же пример, используя<
:regex_compiler<>
>
sregex_compiler compiler; sregex re = compiler.compile( "this|that", regex_constants::icase );
<
реализуется в терминах<basic_regex<>::compile()
>
.regex_compiler<>
>
Поскольку динамический синтаксис не ограничен правилами для корректных выражений C++, мы можем свободно использовать знакомый синтаксис для динамических регексов. По этой причине синтаксис, используемый для динамических регексов, следует примеру, предложенному Джоном Мэддоком, чтобы добавить регулярные выражения в Стандартную библиотеку. По сути, это синтаксис, стандартизированныйECMAScript, с незначительными изменениями в поддержке интернационализации.
Поскольку синтаксис полностью документирован в другом месте, я просто отсылаю вас к существующим стандартам, а не дублирую спецификацию здесь.
Как и в случае с статическими регексами, динамические регексы поддерживают интернационализацию, позволяя указать другой<std::locale
>. Для этого нужно использовать<
. Класс<regex_compiler<>
>
имеет функцию<regex_compiler<>
>imbue()
>. После того, как вы пропитали<
объект пользовательским<regex_compiler<>
>std::locale
>, все объекты regex, собранные этим<
, будут использовать эту локализацию. Например:regex_compiler<>
>
std::locale my_locale = /* initialize your locale object here */; sregex_compiler compiler; compiler.imbue( my_locale ); sregex re = compiler.compile( "\\w+|\\d+" );
Этот регекс будет использовать<my_locale
>при оценке внутренних наборов символов<"\\w"
>и<"\\d"
>.
После создания объекта регекса можно использовать алгоритмы<
и<regex_match()
>
для поиска шаблонов в строках. Эта страница охватывает основы сопоставления и поиска регексов. Во всех случаях, если вы знакомы с тем, как работают<regex_search()
>
и<regex_match()
>
в библиотекеBoost.Regex, версии xpressive работают одинаково.regex_search()
>
Алгоритм<
проверяет, соответствует ли регекс заданному входу.regex_match()
>
![]() |
Warning |
---|---|
Алгоритм< |
Вход может представлять собой двунаправленный диапазон, такой как<std::string
>, нулевая строка в стиле C или пара итераторов. Во всех случаях тип итератора, используемого для прохождения входной последовательности, должен соответствовать типу итератора, используемому для объявления объекта регекса. (Вы можете использовать таблицу вQuick Start, чтобы найти правильный тип регекса для вашего итератора.)
cregex cre = +_w; // this regex can match C-style strings sregex sre = +_w; // this regex can match std::strings if( regex_match( "hello", cre ) ) // OK { /*...*/ } if( regex_match( std::string("hello"), sre ) ) // OK { /*...*/ } if( regex_match( "hello", sre ) ) // ERROR! iterator mis-match! { /*...*/ }
Алгоритм<
необязательно принимает структуру<regex_match()
>
в качестве параметра. Если дано, алгоритм<match_results<>
>
заполняет структуру<regex_match()
>
информацией о том, какие части регекса совпадали с какими частями ввода.match_results<>
>
cmatch what; cregex cre = +(s1= _w); // store the results of the regex_match in "what" if( regex_match( "hello", what, cre ) ) { std::cout << what[1] << '\n'; // prints "o" }
Алгоритм<
также необязательно принимает битмаску<regex_match()
>
. С<match_flag_type
>
вы можете контролировать определенные аспекты оценки матча. См. ссылку<match_flag_type
>
для полного списка флагов и их значений.match_flag_type
>
std::string str("hello"); sregex sre = bol >> +_w; // match_not_bol means that "bol" should not match at [begin,begin) if( regex_match( str.begin(), str.end(), sre, regex_constants::match_not_bol ) ) { // should never get here!!! }
Нажмитездесь, чтобы увидеть полную примерную программу, которая показывает, как использовать<
. И проверьте ссылку<regex_match()
>
, чтобы увидеть полный список доступных перегрузок.regex_match()
>
Используйте<
, когда вы хотите знать, содержит ли входная последовательность подпоследовательность, которая соответствует регексу.<regex_search()
>
будет пытаться сопоставить регекс в начале входной последовательности и сканировать вперед в последовательности, пока он либо не найдет совпадение, либо не исчерпает последовательность.regex_search()
>
Во всех других отношениях<
ведет себя как<regex_search()
>
(см. выше). В частности, он может работать на двунаправленном диапазоне, таком как<regex_match()
>std::string
>, нулевые струны в стиле C или диапазоны итераторов. Необходимо также позаботиться о том, чтобы тип итератора вашего регекса соответствовал типу итератора вашей входной последовательности. Как и в случае с<
, вы можете дополнительно предоставить<regex_match()
>
структуру для получения результатов поиска и<match_results<>
>
битмаску для управления оценкой соответствия.match_flag_type
>
Нажмитездесь, чтобы увидеть полную примерную программу, которая показывает, как использовать<
. И проверьте ссылку<regex_search()
>
, чтобы увидеть полный список доступных перегрузок.regex_search()
>
Иногда недостаточно просто знать, был ли успех<
или<regex_match()
>
. Если вы передадите объект типа<regex_search()
>
<match_results<>
>
или<regex_match()
>
, то после успешного завершения алгоритма<regex_search()
>
будет содержать дополнительную информацию о том, какие части регекса совпадали с какими частями последовательности. В Perl эти подпоследовательности называютсяобратными ссылками, и они хранятся в переменных<match_results<>
>$1
>,<$2
>и т.д. В xpressive они являются объектами типа<
, и они хранятся в структуре<sub_match<>
>
, которая действует как вектор<match_results<>
>
объектов.sub_match<>
>
Итак, вы передали объект<
алгоритму регекса, и алгоритм преуспел. Теперь вы хотите изучить результаты. Большая часть того, что вы будете делать с объектом<match_results<>
>
, индексируется в него, чтобы получить доступ к его внутренним объектам<match_results<>
>
, но есть несколько других вещей, которые вы можете сделать с объектом<sub_match<>
>
.match_results<>
>
В таблице ниже показано, как получить доступ к информации, хранящейся в объекте<
под названием<match_results<>
>what
>.
Table 43.5. match_results<> Accessors
Аксессуар |
последствия |
---|---|
< |
Returns the number of sub-matches, which is always greater than zero after a successful match because the full match is stored in the zero-th sub-match. |
|
Returns the n-th sub-match. |
|
Returns the length of the n-th sub-match.
Same as |
|
Returns the offset into the input sequence at which the n-th sub-match begins. |
|
Returns a |
|
Возвращает< |
|
Returns a |
|
Returns the |
Вы можете сделать больше с объектом<
, но это будет покрыто, когда мы говорим ограмматиках и вложенных матчах.match_results<>
>
Когда вы индексируете<
объект, вы получаете обратно<match_results<>
>
объект. А<sub_match<>
>
— это в основном пара итераторов. Он определяется следующим образом:sub_match<>
>
template< class BidirectionalIterator > struct sub_match : std::pair< BidirectionalIterator, BidirectionalIterator > { bool matched; // ... };
Поскольку он наследует оповещение от<std::pair<>
>,<
имеет<sub_match<>
>first
>и<second
>элементы данных типа<BidirectionalIterator
>. Таковы начало и конец этой подпоследовательности<
.<sub_match<>
>
также имеет Булевой<sub_match<>
>matched
>элемент данных, что верно, если этот<
участвовал в полном матче.sub_match<>
>
Следующая таблица показывает, как вы можете получить доступ к информации, хранящейся в объекте<
под названием<sub_match<>
>sub
>.
Table 43.6. sub_match<> Accessors
Аксессуар |
последствия |
---|---|
< |
Returns the length of the sub-match. Same as |
|
Возвращает< |
|
Выполняет сравнение струн между подматчем и< |
Результаты хранятся в виде итераторов во входной последовательности. Все, что отменяет входную последовательность, отменяет результаты матча. Например, если вы соответствуете объекту<std::string
>, результаты действительны только до следующего вызова функции неконст-члена этого объекта<std::string
>. После этого результаты, проведенные объектом<
, недействительны. Не используй их!match_results<>
>
Регулярные выражения хороши не только для поиска текста, но и для манипулирования им. Одной из наиболее распространенных задач манипулирования текстом является поиск и замена. xpressive предоставляет алгоритм поиска и замены<
.regex_replace()
>
Выполнение поиска и замены с помощью<
просто. Все, что вам нужно, это последовательность ввода, объект регекса и строка формата или объект форматтера. Существует несколько версий алгоритма<regex_replace()
>
. Некоторые принимают входную последовательность в качестве двунаправленного контейнера, такого как<regex_replace()
>std::string
>, и возвращают результат в новый контейнер того же типа. Другие принимают вход как нулевую завершенную строку и возвращают<std::string
>. Третьи принимают входную последовательность как пару итераторов и записывают результат в выходной итератор. Замена может быть указана как строка с последовательностями формата или как объект формататора. Ниже приведены несколько простых примеров использования струнных замен.
std::string input("This is his face"); sregex re = as_xpr("his"); // find all occurrences of "his" ... std::string format("her"); // ... and replace them with "her" // use the version of regex_replace() that operates on strings std::string output = regex_replace( input, re, format ); std::cout << output << '\n'; // use the version of regex_replace() that operates on iterators std::ostream_iterator< char > out_iter( std::cout ); regex_replace( out_iter, input.begin(), input.end(), re, format );
Вышеупомянутая программа распечатывает следующее:
Ther is her face Ther is her face
Обратите внимание, чтовсеслучаи<"his"
>были заменены<"her"
>.
Нажмитездесь, чтобы увидеть полную примерную программу, которая показывает, как использовать<
. И проверьте ссылку<regex_replace()
>
, чтобы увидеть полный список доступных перегрузок.regex_replace()
>
Алгоритм<
использует дополнительный параметр битмаски для управления форматированием. Возможные значения битмаски:regex_replace()
>
Table 43.7. Format Flags
Флаг |
значение |
---|---|
< |
Recognize the ECMA-262 format sequences (see below). |
|
Only replace the first match, not all of them. |
|
Не копируйте части входной последовательности, которые не соответствовали регексу выходной последовательности. |
|
Treat the format string as a literal; that is, don't recognize any escape sequences. |
< |
Recognize the Perl format sequences (see below). |
< |
Recognize the sed format sequences (see below). |
|
В дополнение к последовательностям формата Perl, распознайте некоторые последовательности формата Boost. |
Эти флаги живут в пространстве имен<xpressive::regex_constants
>. Если параметр замещения является функциональным объектом вместо строки, флаги<format_literal
>,<format_perl
>,<format_sed
>и<format_all
>игнорируются.
Если вы не указали диалект строк замены с одним из флагов формата выше, вы получите диалект, определенный ECMA-262, стандартом для ECMAScript. В таблице ниже показаны последовательности выхода, распознаваемые в режиме ECMA-262.
Table 43.8. Format Escape Sequences
Последовательность побега |
значение |
---|---|
|
соответствующий субматч |
|
the full match |
|
Префикс матча |
|
the match suffix |
< |
a literal |
Любая другая последовательность, начинающаяся с<'$'
>, просто представляет себя. Например, если строка формата<"$a"
>, то<"$a"
>будет вставлена в выходную последовательность.
При указании флага<format_sed
>на<
распознаются следующие последовательности побега:regex_replace()
>
Table 43.9. Sed Format Escape Sequences
Последовательность побега |
значение |
---|---|
|
The corresponding sub-match |
< |
the full match |
|
A literal |
< |
Буквально< |
< |
Буквально< |
|
Буквально< |
|
Буквально< |
< |
A literal |
< |
A literal |
< |
A literal |
|
A literal |
< |
The control character |
При указании флага<format_perl
>на<
распознаются следующие последовательности выхода:regex_replace()
>
Table 43.10. Perl Format Escape Sequences
Последовательность побега |
значение |
---|---|
|
соответствующий субматч |
|
the full match |
|
Префикс матча |
|
the match suffix |
< |
a literal |
|
A literal |
< |
Буквально< |
< |
Буквально< |
|
Буквально< |
|
Буквально< |
< |
A literal |
< |
A literal |
< |
A literal |
|
A literal |
< |
The control character |
< |
Make the next character lowercase |
< |
Сделайте остальную часть нижнего регистра замены до следующего< |
|
Make the next character uppercase |
|
Make the rest of the substitution uppercase until the next |
< |
Прекратить< |
|
The corresponding sub-match |
< |
Имяимя |
При указании флага<format_all
>на<
распознанные последовательности побега те же, что и выше для<regex_replace()
>format_perl
>. Кроме того, признаются условные выражения следующей формы:
?Ntrue-expression:false-expression
гдеN— десятичная цифра, представляющая подматчевую. Если в полном матче участвовал соответствующий субматч, то заменаистинное выражение. В противном случае этоложное выражение. В этом режиме можно использовать парены<()
>для группировки. Если вы хотите буквальный paren, вы должны избежать его как<\(
>.
Строки формата не всегда достаточно выразительны для всех ваших потребностей в замене текста. Рассмотрим простой пример желания сопоставить входные строки с выходными строками, как вы можете сделать с переменными среды. Вместо форматастрокидля этого вы использовали бы формататоробъекта. Рассмотрим следующий код, который находит встроенные переменные среды формы<"$(XYZ)"
>и вычисляет строку замены, просматривая переменную среды на карте.
#include <map> #include <string> #include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost; using namespace xpressive; std::map<std::string, std::string> env; std::string const &format_fun(smatch const &what) { return env[what[1].str()]; } int main() { env["X"] = "this"; env["Y"] = "that"; std::string input("\"$(X)\" has the value \"$(Y)\""); // replace strings like "$(XYZ)" with the result of env["XYZ"] sregex envar = "$(" >> (s1 = +_w) >> ')'; std::string output = regex_replace(input, envar, format_fun); std::cout << output << std::endl; }
В этом случае мы используем функцию<format_fun()
>для вычисления строки замены на лету. Он принимает объект<
, который содержит результаты текущего матча.<match_results<>
>format_fun()
>использует первый подматч в качестве ключа к глобальной<env
>карте. Вышеприведенный код отображает:
"this" has the value "that"
Формататор не обязательно должен быть обычной функцией. Это может быть объект класса. И вместо того, чтобы возвращать строку, она может принять выходной итератор, в который она записывает замену. Рассмотрим следующее, которое функционально эквивалентно вышеизложенному.
#include <map> #include <string> #include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost; using namespace xpressive; struct formatter { typedef std::map<std::string, std::string> env_map; env_map env; template<typename Out> Out operator()(smatch const &what, Out out) const { env_map::const_iterator where = env.find(what[1]); if(where != env.end()) { std::string const &sub = where->second; out = std::copy(sub.begin(), sub.end(), out); } return out; } }; int main() { formatter fmt; fmt.env["X"] = "this"; fmt.env["Y"] = "that"; std::string input("\"$(X)\" has the value \"$(Y)\""); sregex envar = "$(" >> (s1 = +_w) >> ')'; std::string output = regex_replace(input, envar, fmt); std::cout << output << std::endl; }
Формататор должен быть вызывающим объектом — функцией или функциональным объектом — который имеет одну из трех возможных подписей, подробно описанных в таблице ниже. Для таблицы<fmt
>— указатель функции или объект функции,<what
>— объект<
,<match_results<>
>out
>— итератор вывода,<flags
>— значение<regex_constants::match_flag_type
>:
Table 43.11. Formatter Signatures
Формат вызова |
Тип возврата |
Семантика |
---|---|---|
< |
Range of characters (e.g. |
Струна, соответствующая регексу, заменяется строкой, возвращаемой форматером. |
|
OutputIterator |
The formatter writes the replacement string into |
|
OutputIterator |
The formatter writes the replacement string into |
Помимо форматастроки формататораобъектов,<
также принимает выражения форматера. Форматтер - это лямбда-выражение, которое генерирует строку. Он использует тот же синтаксис, что и длясемантических действий, которые рассматриваются позже. Приведенный выше пример, который использует<regex_replace()
>
для замены строк переменных среды, повторяется здесь с использованием выражения форматтера.regex_replace()
>
#include <map> #include <string> #include <iostream> #include <boost/xpressive/xpressive.hpp> #include <boost/xpressive/regex_actions.hpp> using namespace boost::xpressive; int main() { std::map<std::string, std::string> env; env["X"] = "this"; env["Y"] = "that"; std::string input("\"$(X)\" has the value \"$(Y)\""); sregex envar = "$(" >> (s1 = +_w) >> ')'; std::string output = regex_replace(input, envar, ref(env)[s1]); std::cout << output << std::endl; }
В приведенном выше выражении форматтер<ref(env)[s1]
>. Это означает использование значения первого подматча<s1
>в качестве ключа на карте<env
>. Цель<xpressive::ref()
>здесь состоит в том, чтобы сделать ссылку на<env
>локальную переменнуюленивуютак, чтобы операция индекса откладывалась до тех пор, пока мы не узнаем, чем заменить<s1
>.
<
— это нож Гинсу в мире манипулирования текстом. Это ломтики! Это кости! В этом разделе описывается, как использовать высококонфигурируемые<regex_token_iterator<>
>
для сокращения входных последовательностей.regex_token_iterator<>
>
Вы инициализируете<
с входной последовательностью, регексом и некоторыми дополнительными параметрами конфигурации.<regex_token_iterator<>
>
будет использовать<regex_token_iterator<>
>
, чтобы найти первое место в последовательности, которая соответствует регексу. При упоминании<regex_search()
>
возвращаеттокенв форме<regex_token_iterator<>
>std::basic_string<>
>. Какую строку он возвращает, зависит от параметров конфигурации. По умолчанию она возвращает строку, соответствующую полному совпадению, но она также может возвращать строку, соответствующую определенному отмеченному субвыражению, или даже часть последовательности, котораяне соответствовала. Когда вы увеличите<
, он перейдет к следующему токену. Какой токен следующий, зависит от параметров конфигурации. Это может быть просто другое обозначенное суб-выражение в текущем матче, или это может быть часть или весь следующий матч. Или это может быть та часть, котораяне соответствовала.regex_token_iterator<>
>
Как видите,<
может многое сделать. Это затрудняет описание, но некоторые примеры должны прояснить это.regex_token_iterator<>
>
В этом примере<
последовательность измельчается в ряд токенов, состоящих из слов.regex_token_iterator<>
>
std::string input("This is his face"); sregex re = +_w; // find a word // iterate over all the words in the input sregex_token_iterator begin( input.begin(), input.end(), re ), end; // write all the words to std::cout std::ostream_iterator< std::string > out_iter( std::cout, "\n" ); std::copy( begin, end, out_iter );
Эта программа отображает следующее:
This is his face
Этот пример также использует<
, чтобы разбить последовательность на ряд токенов, состоящих из слов, но он использует регекс в качестве делимитера. Когда мы передаем<regex_token_iterator<>
>-1
>в качестве последнего параметра конструктору<
, он инструктирует итератор маркеров рассматривать в качестве маркеров те части входа, которыене соответствовалирегекса.regex_token_iterator<>
>
std::string input("This is his face"); sregex re = +_s; // find white space // iterate over all non-white space in the input. Note the -1 below: sregex_token_iterator begin( input.begin(), input.end(), re, -1 ), end; // write all the words to std::cout std::ostream_iterator< std::string > out_iter( std::cout, "\n" ); std::copy( begin, end, out_iter );
Эта программа отображает следующее:
This is his face
Этот пример также использует<
, чтобы разделить последовательность, содержащую кучу дат, на ряд токенов, состоящих только из лет. Когда мы передаем положительное целое число<regex_token_iterator<>
>N
>в качестве последнего параметра конструктору<
, он инструктирует итератор токенов рассматривать в качестве токенов только<regex_token_iterator<>
>N
>-е отмеченное подвыражение каждого матча.
std::string input("01/02/2003 blahblah 04/23/1999 blahblah 11/13/1981"); sregex re = sregex::compile("(\\d{2})/(\\d{2})/(\\d{4})"); // find a date // iterate over all the years in the input. Note the 3 below, corresponding to the 3rd sub-expression: sregex_token_iterator begin( input.begin(), input.end(), re, 3 ), end; // write all the words to std::cout std::ostream_iterator< std::string > out_iter( std::cout, "\n" ); std::copy( begin, end, out_iter );
Эта программа отображает следующее:
2003 1999 1981
Этот пример похож на предыдущий, за исключением того, что вместо токенизации только лет эта программа превращает дни, месяцы и годы в токены. Когда мы передаем массив целых чисел<{I,J,...}
>в качестве последнего параметра конструктору<
, он инструктирует итератор токенов рассматривать в качестве токенов<regex_token_iterator<>
>I
>-th,<J
>-th и т.д. отмеченное суб-выражение каждого матча.
std::string input("01/02/2003 blahblah 04/23/1999 blahblah 11/13/1981"); sregex re = sregex::compile("(\\d{2})/(\\d{2})/(\\d{4})"); // find a date // iterate over the days, months and years in the input int const sub_matches[] = { 2, 1, 3 }; // day, month, year sregex_token_iterator begin( input.begin(), input.end(), re, sub_matches ), end; // write all the words to std::cout std::ostream_iterator< std::string > out_iter( std::cout, "\n" ); std::copy( begin, end, out_iter );
Эта программа отображает следующее:
02 01 2003 23 04 1999 13 11 1981
Массив<sub_matches
>предписывает<
сначала принять значение 2-го подматча, затем 1-го подматча и, наконец, 3-го. Пополнение итератора снова дает ему указание использовать<regex_token_iterator<>
>
снова, чтобы найти следующий матч. В этот момент процесс повторяется — итератор токенов принимает значение 2-го подматча, затем 1-го и так далее.regex_search()
>
Для сложных регулярных выражений, работа с пронумерованными захватами может быть болью. Подсчет левых скобок, чтобы выяснить, какой захват ссылаться, не весело. Менее забавным является тот факт, что простое редактирование обычного выражения может привести к тому, что захвату будет присвоен новый номер, недействительный код, который ссылается на него на старый номер.
Другие двигатели регулярного выражения решают эту проблему с помощью функции, называемой, называемой захватами. Эта функция позволяет назначить имя для захвата и ссылаться на захват по имени, а не по номеру. Xpressive также поддерживает именованные захваты, как в динамических, так и в статических регексах.
Для динамических регулярных выражений xpressive следует примеру других популярных движков регекса с синтаксисом именованных захватов. Вы можете создать именованный захват с<"(?P<xxx>...)"
>и вернуться к этому захвату с<"(?P=xxx)"
>. Вот, например, регулярное выражение, которое создает именованный захват и ссылается на него:
// Create a named capture called "char" that matches a single // character and refer back to that capture by name. sregex rx = sregex::compile("(?P<char>.)(?P=char)");
Эффектом вышеупомянутого регулярного выражения является нахождение первого удвоенного характера.
После того, как вы выполнили операцию поиска с использованием регекса с именованными захватами, вы можете получить доступ к названному захвату через<
объект, использующий название захвата.match_results<>
>.
std::string str("tweet"); sregex rx = sregex::compile("(?P<char>.)(?P=char)"); smatch what; if(regex_search(str, what, rx)) { std::cout << "char = " << what["char"] << std::endl; }
Вышеприведенный код отображает:
char = e
Вы также можете обратиться к именованному захвату из строки замены. Синтаксис для этого<"\\g<xxx>"
>. Ниже приведен код, который показывает, как использовать именованные захваты при замене строки.
std::string str("tweet"); sregex rx = sregex::compile("(?P<char>.)(?P=char)"); str = regex_replace(str, rx, "**\\g<char>**", regex_constants::format_perl); std::cout << str << std::endl;
Обратите внимание, что вы должны указать<format_perl
>при использовании названных захватов. Только синтаксис perl распознает синтаксис<"\\g<xxx>"
>. Вышеприведенный код отображает:
tw**e**t
Если вы используете статические регулярные выражения, создание и использование именованных захватов еще проще. Вы можете использовать тип<
, чтобы создать переменную, которую вы можете использовать, как<mark_tag
>s1
>,<s2
>и друзья, но с именем, которое более значимо. Ниже приведен пример того, как будет выглядеть вышеприведенный пример с использованием статических регексов:
mark_tag char_(1); // char_ is now a synonym for s1 sregex rx = (char_= _) >> char_;
После операции совпадения вы можете использовать<mark_tag
>для индексации в<
для доступа к названному захвату:match_results<>
>
std::string str("tweet"); mark_tag char_(1); sregex rx = (char_= _) >> char_; smatch what; if(regex_search(str, what, rx)) { std::cout << what[char_] << std::endl; }
Вышеприведенный код отображает:
char = e
При замене строк<
вы можете использовать именованные захваты для созданиявыражений формата, как указано ниже:regex_replace()
>
std::string str("tweet"); mark_tag char_(1); sregex rx = (char_= _) >> char_; str = regex_replace(str, rx, "**" + char_ + "**"); std::cout << str << std::endl;
Вышеприведенный код отображает:
tw**e**t
![]() |
Note |
---|---|
Вы должны включить< |
Одним из ключевых преимуществ представления регексов как выражений C++ является возможность легко ссылаться на другой код C++ и данные из регекса. Это позволяет программировать идиомы, которые невозможны с другими библиотеками регулярных выражений. Особо следует отметить способность одного регекса ссылаться на другой, что позволяет создавать грамматику из регулярных выражений. В этом разделе описывается, как встроить один регекс в другой по значению и по ссылке, как объекты регекса ведут себя, когда они относятся к другим регексам, и как получить доступ к дереву результатов после успешного анализа.
<
объект имеет значение семантики. Когда объект регекса появляется с правой стороны в определении другого регекса, это как если бы регекс был встроен в значение; то есть копия вложенного регекса хранится в прилагающем регексе. Внутренний регекс вызывается внешним регексом во время сопоставления шаблонов. Внутренний регекс полностью участвует в матче, отслеживая его по мере необходимости, чтобы сделать матч успешным.basic_regex<>
>
Рассмотрим текстовый редактор, который имеет функцию regex-find с опцией «целое слово». Вы можете реализовать это с помощью xpressive следующим образом:
find_dialog dlg; if( dialog_ok == dlg.do_modal() ) { std::string pattern = dlg.get_text(); // the pattern the user entered bool whole_word = dlg.whole_word.is_checked(); // did the user select the whole-word option? sregex re = sregex::compile( pattern ); // try to compile the pattern if( whole_word ) { // wrap the regex in begin-word / end-word assertions re = bow >> re >> eow; } // ... use re ... }
Посмотрите внимательно на эту линию:
// wrap the regex in begin-word / end-word assertions re = bow >> re >> eow;
Эта линия создает новый регекс, который встраивает старый регекс по стоимости. Затем новый регекс возвращается к исходному регексу. Поскольку копия старого регекса была сделана с правой стороны, это работает так, как вы можете ожидать: новый регекс имеет поведение старого регекса, завернутого в утверждения начального и конечного слов.
![]() |
Note |
---|---|
Обратите внимание, что< |
Если вы хотите иметь возможность создавать рекурсивные регулярные выражения и контекстно-свободные грамматики, встраивания регекса по значению недостаточно. Вы должны быть в состоянии сделать свои регулярные выражения самореферентными. Большинство двигателей с регулярным выражением не дают вам такой мощности, но впечатляет.
![]() |
Tip |
---|---|
Теоретики-компьютерщики правильно укажут на то, что самореферентное регулярное выражение не является «регулярным», поэтому в строгом смысле «хпрессивный» на самом деле не является движком регулярного выражения. Но, как однажды сказал Ларри Уолл, «термин [регулярное выражение] вырос с возможностями наших двигателей, соответствующих шаблонам, поэтому я не буду пытаться бороться с лингвистической необходимостью здесь». |
Рассмотрим следующий код, который использует помощник<by_ref()
>для определения рекурсивного регулярного выражения, соответствующего сбалансированным вложенным скобкам:
sregex parentheses; parentheses // A balanced set of parentheses ... = '(' // is an opening parenthesis ... >> // followed by ... *( // zero or more ... keep( +~(set='(',')') ) // of a bunch of things that are not parentheses ... | // or ... by_ref(parentheses) // a balanced set of parentheses ) // (ooh, recursion!) ... >> // followed by ... ')' // a closing parenthesis ;
Совпадение сбалансированных, вложенных тегов является важной задачей обработки текста, и это то, что «классические» регулярные выражения не могут сделать. Помощник<by_ref()
>делает это возможным. Он позволяет встраивать один объект в другойпосредством ссылки. Поскольку правая сторона удерживает<parentheses
>посредством ссылки, назначение правой стороны обратно на<parentheses
>создает цикл, который будет выполняться рекурсивно.
Как только мы позволяем себе ссылаться в наших регулярных выражениях, джинн выходит из бутылки, и возможны всевозможные забавные вещи. В частности, теперь мы можем строить грамматику из регулярных выражений. Давайте посмотрим на пример грамматики учебника: скромный калькулятор.
sregex group, factor, term, expression; group = '(' >> by_ref(expression) >> ')'; factor = +_d | group; term = factor >> *(('*' >> factor) | ('/' >> factor)); expression = term >> *(('+' >> term) | ('-' >> term));
Регекс<expression
>делает нечто весьма примечательное для обычного выражения: он соответствует математическим выражениям. Например, если бы входная строка была<"foo 9*(10+3) bar"
>, этот шаблон соответствовал бы<"9*(10+3)"
>. Он соответствует только хорошо сформированным математическим выражениям, где скобки сбалансированы, а операторы фиксации имеют по два аргумента. Не пытайтесь сделать это с помощью обычного двигателя!
Давайте более подробно рассмотрим эту грамматику регулярного выражения. Обратите внимание, что он цикличен:<expression
>реализован в терминах<term
>, который реализован в терминах<factor
>, который реализован в терминах<group
>, который реализован в терминах<expression
>, закрывая цикл. В общем, способ определения циклической грамматики состоит в том, чтобы вперед объявить объекты regex и вставить путем ссылки те регулярные выражения, которые еще не были инициализированы. В приведенной выше грамматике есть только одно место, где нужно ссылаться на еще не инициализированный объект регекса: определение<group
>. В этом месте мы используем<by_ref()
>для встраивания<expression
>посредством ссылки. Во всех остальных местах достаточно встроить другие объекты регекса по стоимости, так как они уже инициализированы и их значения не изменятся.
![]() |
Tip |
---|---|
Встроен по стоимости, если это возможно |
Используя<
, вы также можете создавать грамматики из динамических регулярных выражений. Вы делаете это, создавая именованные регексы и ссылаясь на другие регексы по имени. Каждый экземпляр<regex_compiler<>
>
сохраняет отображение от имен до регексов, которые были созданы с ним.regex_compiler<>
>
Вы можете создать именованный динамический регекс, предваряя свой регекс<"(?$name=)"
>, гдеимяявляется именем регекса. Вы можете обратиться к названному регексу из другого регекса с<"(?$name)"
>. Названный регекс не должен существовать в то время, когда он упоминается в другом регексе, но он должен существовать к тому времени, когда вы используете регекс.
Ниже приведен фрагмент кода, который использует динамические грамматики для реализации примера калькулятора сверху.
using namespace boost::xpressive; using namespace regex_constants; sregex expr; { sregex_compiler compiler; syntax_option_type x = ignore_white_space; compiler.compile("(? $group = ) \\( (? $expr ) \\) ", x); compiler.compile("(? $factor = ) \\d+ | (? $group ) ", x); compiler.compile("(? $term = ) (? $factor )" " ( \\* (? $factor ) | / (? $factor ) )* ", x); expr = compiler.compile("(? $expr = ) (? $term )" " ( \\+ (? $term ) | - (? $term ) )* ", x); } std::string str("foo 9*(10+3) bar"); smatch what; if(regex_search(str, what, expr)) { // This prints "9*(10+3)": std::cout << what[0] << std::endl; }
Как и в случае с грамматикой статического регекса, вложенные вызовы регекса создают вложенные результаты соответствия (см.).Вложенные результатыниже. Результатом является полное дерево разбора для струны, которая соответствует. В отличие от статических регексов, динамические регексы всегда встроены посредством ссылки, а не значения.
Примеры калькулятора выше поднимают ряд очень сложных проблем управления памятью. Каждый из четырех объектов регекса относится друг к другу, некоторые прямо, а некоторые косвенно, некоторые по значению, а некоторые по ссылке. Что, если мы вернем одного из них из функции и позволим другим выйти из-под контроля? Что станет со ссылками? Ответ заключается в том, что объекты регекса считаются внутренними ссылками, так что они сохраняют свои объекты регекса живыми до тех пор, пока они в них нуждаются. Таким образом, передача объекта регекса по значению никогда не является проблемой, даже если она относится к другим объектам регекса, которые вышли из сферы действия.
Те из вас, кто занимался подсчетом ссылок, вероятно, знакомы с его ахиллесовой пятой: циклическими ссылками. Если объекты регекса подсчитываются, что происходит с циклами, подобными тем, которые созданы в примерах калькулятора? Они просочились? Ответ - нет, они не просочились. Объект<
имеет сложный код отслеживания ссылок, который гарантирует, что даже циклические грамматики регекса очищаются, когда последняя внешняя ссылка исчезает. Так что не волнуйся. Создавайте циклические грамматики, передайте объекты регекса и копируйте их все, что хотите. Это быстро и эффективно и гарантированно не утечка или не приводит к свисанию ссылок.basic_regex<>
>
Вложенные регулярные выражения поднимают вопрос о подматчевом определении. Если и внутренний, и внешний регекс пишут и читают из одного и того же вектора подсоответствия, то наступает хаос. Внутренний регекс будет топтать на подматчи, написанные внешним регексом. Например, что это делает?
sregex inner = sregex::compile( "(.)\\1" ); sregex outer = (s1= _) >> inner >> s1;
Автор, вероятно, не намеревался, чтобы внутренний регекс перезаписывал подматч, написанный внешним регексом. Проблема особенно остро стоит, когда внутренний регекс принимается от пользователя в качестве ввода. Автор не имеет возможности узнать, будет ли внутренний регекс топтать вектор подматчевого состояния или нет. Это явно неприемлемо.
Вместо этого на самом деле происходит то, что каждый вызов вложенного регекса получает свой собственный охват. Подматчи относятся к этой сфере. То есть, каждый вложенный вызов регекса получает свою собственную копию вектора подматча, чтобы играть, поэтому нет возможности для внутреннего регекса топать на подматчи внешнего регекса. Так, например, регекс<outer
>, определенный выше, будет соответствовать<"ABBA"
>.
Если вложенные регексы имеют свои собственные подматчи, должен быть способ доступа к ним после успешного матча. На самом деле, есть. После<
или<regex_match()
>
структура<regex_search()
>
ведет себя как голова дерева вложенных результатов. Класс<match_results<>
>
обеспечивает функцию члена<match_results<>
>nested_results()
>, которая возвращает упорядоченную последовательность структур<
, представляющую результаты вложенных регексов. Порядок вложенных результатов такой же, как и порядок, в котором вложенные объекты регекса совпадают.match_results<>
>
Возьмем в качестве примера регекс для сбалансированных, вложенных скобок, которые мы видели ранее:
sregex parentheses; parentheses = '(' >> *( keep( +~(set='(',')') ) | by_ref(parentheses) ) >> ')'; smatch what; std::string str( "blah blah( a(b)c (c(e)f (g)h )i (j)6 )blah" ); if( regex_search( str, what, parentheses ) ) { // display the whole match std::cout << what[0] << '\n'; // display the nested results std::for_each( what.nested_results().begin(), what.nested_results().end(), output_nested_results() ); }
Эта программа отображает следующее:
( a(b)c (c(e)f (g)h )i (j)6 ) (b) (c(e)f (g)h ) (e) (g) (j)
Здесь вы можете увидеть, как вложены результаты и что они хранятся в том порядке, в котором они найдены.
![]() |
Tip |
---|---|
См. определениеoutput_nested_resultsв разделеПримеры. |
Иногда регекс имеет несколько вложенных объектов регекса, и вы хотите знать, какой результат соответствует объекту регекса. Вот где<basic_regex<>::regex_id()
>и<match_results<>::regex_id()
>пригодятся. При повторении по вложенным результатам вы можете сравнить идентификатор регекса из результатов с идентификатором объекта регекса, который вас интересует.
Чтобы сделать это немного проще, xpressive предоставляет предикат, чтобы упростить итерацию результатов, которые соответствуют определенному вложенному регексу. Он называется<regex_id_filter_predicate
>и предназначен для использования сBoost.Iterator. Вы можете использовать его следующим образом:
sregex name = +alpha; sregex integer = +_d; sregex re = *( *_s >> ( name | integer ) ); smatch what; std::string str( "marsha 123 jan 456 cindy 789" ); if( regex_match( str, what, re ) ) { smatch::nested_results_type::const_iterator begin = what.nested_results().begin(); smatch::nested_results_type::const_iterator end = what.nested_results().end(); // declare filter predicates to select just the names or the integers sregex_id_filter_predicate name_id( name.regex_id() ); sregex_id_filter_predicate integer_id( integer.regex_id() ); // iterate over only the results from the name regex std::for_each( boost::make_filter_iterator( name_id, begin, end ), boost::make_filter_iterator( name_id, end, end ), output_result ); std::cout << '\n'; // iterate over only the results from the integer regex std::for_each( boost::make_filter_iterator( integer_id, begin, end ), boost::make_filter_iterator( integer_id, end, end ), output_result ); }
где<output_results
>— простая функция, которая принимает<smatch
>и отображает полное соответствие. Обратите внимание, как мы используем<regex_id_filter_predicate
>вместе с<basic_regex<>::regex_id()
>и<boost::make_filter_iterator()
>изBoost.Iteratorдля выбора только тех результатов, которые соответствуют конкретному вложенному регексу. Эта программа отображает следующее:
marsha jan cindy 123 456 789
Представьте, что вы хотите разобрать входную строку и построить из нее<std::map<>
>. Для чего-то подобного соответствия обычному выражению недостаточно. Вы хотитесделать что-то, когда части вашего обычного выражения совпадают. Xpressive позволяет прикреплять семантические действия к частям статических регулярных выражений. Этот раздел показывает, как.
Рассмотрим следующий код, который использует семантические действия xpressive для разбора строки пар слова / целого числа и вставляет их в<std::map<>
>. Это описано ниже.
#include <string> #include <iostream> #include <boost/xpressive/xpressive.hpp> #include <boost/xpressive/regex_actions.hpp> using namespace boost::xpressive; int main() { std::map<std::string, int> result; std::string str("aaa=>1 bbb=>23 ccc=>456"); // Match a word and an integer, separated by =>, // and then stuff the result into a std::map<> sregex pair = ( (s1= +_w) >> "=>" >> (s2= +_d) ) [ ref(result)[s1] = as<int>(s2) ]; // Match one or more word/integer pairs, separated // by whitespace. sregex rx = pair >> *(+_s >> pair); if(regex_match(str, rx)) { std::cout << result["aaa"] << '\n'; std::cout << result["bbb"] << '\n'; std::cout << result["ccc"] << '\n'; } return 0; }
Эта программа печатает следующее:
1 23 456
Регулярное выражение<pair
>имеет две части: рисунок и действие. Образец говорит, чтобы соответствовать слову, захватывая его в подматче 1, и целое число, захватывая его в подматче 2, разделенном<"=>"
>. Действие — часть в квадратных скобках:<[
ref(result)[s1]=
as<int>(s2)]
>. Он говорит взять подматч один и использовать его для индексации в<results
>карту, и назначить ему результат преобразования подматча 2 в целое число.
![]() |
Note |
---|---|
Чтобы использовать семантические действия со своими статическими регексами, вы должны< |
Как это работает? Как и остальная часть статического регулярного выражения, часть между скобками является шаблоном выражения. Он кодирует действие и выполняет его позже. Выражение<ref(result)
>создает ленивую отсылку к объекту<result
>. Большее выражение<ref(result)[s1]
>— ленивая операция индекса карты. Позже, когда это действие выполняется,<s1
>заменяется первым<
. Когда<sub_match<>
>as<int>(s2)
>исполняется,<s2
>заменяется вторым<
. Действие<sub_match<>
>as<>
>преобразует свой аргумент в запрашиваемый с помощью Boost. Lexical_cast. Результатом всего действия является вставка нового слова/целой пары в карту.
![]() |
Note |
---|---|
Существует важное различие между функцией< |
В дополнение к подматчевым заполнителям<s1
>,<s2
>и т.д., вы также можете использовать заполнитель<_
>в действии, чтобы ссылаться на строку, соответствующую подвыражению, к которому прикреплено действие. Например, вы можете использовать следующий регекс, чтобы сопоставить кучу цифр, интерпретировать их как целое число и назначить результат локальной переменной:
int i = 0; // Here, _ refers back to all the // characters matched by (+_d) sregex rex = (+_d)[ ref(i) = as<int>(_) ];
Что именно означает прикрепить действие к части регулярного выражения и выполнить совпадение? Когда выполняется действие? Если действие является частью повторного подвыражения, выполняется ли оно один или несколько раз? И если подвыражение изначально совпадает, но в конечном итоге терпит неудачу, потому что остальная часть обычного выражения не соответствует, выполняется ли действие вообще?
Ответ заключается в том, что по умолчанию действия выполняютсялениво. Когда подэкспрессия соответствует строке, ее действие помещается в очередь вместе с текущими значениями любых подматчей, к которым относится действие. Если алгоритм матча должен отступить, действия выскакивают из очереди по мере необходимости. Только после того, как весь регекс успешно совпал, действия фактически выполняются. Все они исполняются сразу, в том порядке, в котором они были добавлены в очередь, как последний шаг перед возвращением<
.regex_match()
>
Например, рассмотрим следующий регекс, который увеличивает счетчик всякий раз, когда он находит цифру.
int i = 0; std::string str("1!2!3?"); // count the exciting digits, but not the // questionable ones. sregex rex = +( _d [ ++ref(i) ] >> '!' ); regex_search(str, rex); assert( i == 2 );
Действие<++ref(i)
>стоит в очереди три раза: один раз за каждую найденную цифру. Но тольковыполненодважды: один раз для каждой цифры, которая предшествует<'!'
>символу. Когда персонаж<'?'
>встречается, алгоритм совпадения отступает, удаляя окончательное действие из очереди.
Когда вы хотите, чтобы семантические действия выполнялись немедленно, вы можете обернуть подвыражение, содержащее действие в<
.<keep()
>keep()
>отключает обратное отслеживание для своего подвыражения, но это также вызывает любые действия, стоящие в очереди подвыражением, чтобы выполнить в конце<keep()
>. Это как если бы суб-выражение в<keep()
>было составлено в независимый объект регекса, и соответствие<keep()
>похоже на отдельное обращение<regex_search()
>. Он соответствует персонажам и выполняет действия, но никогда не отступает и не раскручивается. Например, представьте, что приведенный выше пример был написан следующим образом:
int i = 0; std::string str("1!2!3?"); // count all the digits. sregex rex = +( keep( _d [ ++ref(i) ] ) >> '!' ); regex_search(str, rex); assert( i == 3 );
Мы завернули подвыражение<_d
[++ref(i)]
>в<keep()
>. Теперь, когда этот регекс совпадает с цифрой, действие будет поставлено в очередь, а затем немедленно выполнено, прежде чем мы попытаемся соответствовать<'!'
>персонажу. В этом случае действие выполняется трижды.
![]() |
Note |
---|---|
Подобно< |
До сих пор мы видели, как писать семантические действия, состоящие из переменных и операторов. Но что, если вы хотите получить функцию от семантического действия? Xpressive предоставляет механизм для этого.
Первым шагом является определение типа объекта функции. Вот, например, тип объекта функции, который вызывает<push()
>на его аргумент:
struct push_impl { // Result type, needed for tr1::result_of typedef void result_type; template<typename Sequence, typename Value> void operator()(Sequence &seq, Value const &val) const { seq.push(val); } };
Следующим шагом является использование шаблона<function<>
>для определения функционального объекта под названием<push
>:
// Global "push" function object. function<push_impl>::type const push = {{}};
Инициализация выглядит немного странно, но это потому, что<push
>статично инициализируется. Это означает, что его не нужно строить во время выполнения. Мы можем использовать<push
>в семантических действиях следующее:
std::stack<int> ints; // Match digits, cast them to an int // and push it on the stack. sregex rex = (+_d)[push(ref(ints), as<int>(_))];
Вы заметите, что, делая это таким образом, вызовы функции члена выглядят как обычные вызовы функции. Вы можете написать свое семантическое действие по-другому, чтобы оно выглядело как вызов функции участника:
sregex rex = (+_d)[ref(ints)->*push(as<int>(_))];
Xpressive признает использование<->*
>и относится к этому выражению точно так же, как к приведенному выше.
Когда объект функции должен вернуть тип, который зависит от его аргументов, вы можете использовать шаблон<result<>
>вместо шаблона<result_type
>. Вот, например, объект функции<first
>, который возвращает<first
>элемент<std::pair<>
>или<
:sub_match<>
>
// Function object that returns the // first element of a pair. struct first_impl { template<typename Sig> struct result {}; template<typename This, typename Pair> struct result<This(Pair)> { typedef typename remove_reference<Pair> ::type::first_type type; }; template<typename Pair> typename Pair::first_type operator()(Pair const &p) const { return p.first; } }; // OK, use as first(s1) to get the begin iterator // of the sub-match referred to by s1. function<first_impl>::type const first = {{}};
Как мы видели в примерах выше, мы можем ссылаться на локальные переменные в действиях, используя<xpressive::ref()
>. Любые такие переменные удерживаются путем ссылки регулярным выражением, и следует соблюдать осторожность, чтобы эти ссылки не болтались. Например, в следующем коде ссылка на<i
>остается висеть, когда<bad_voodoo()
>возвращается:
sregex bad_voodoo() { int i = 0; sregex rex = +( _d [ ++ref(i) ] >> '!' ); // ERROR! rex refers by reference to a local // variable, which will dangle after bad_voodoo() // returns. return rex; }
При написании семантических действий вы несете ответственность за то, чтобы все ссылки не болтались. Одним из способов сделать это было бы сделать переменные общими указателями, которые удерживаются регексом по значению.
sregex good_voodoo(boost::shared_ptr<int> pi) { // Use val() to hold the shared_ptr by value: sregex rex = +( _d [ ++*val(pi) ] >> '!' ); // OK, rex holds a reference count to the integer. return rex; }
В приведенном выше коде мы используем<xpressive::val()
>, чтобы удерживать общий указатель по значению. Обычно это не требуется, потому что локальные переменные, появляющиеся в действиях, удерживаются значением по умолчанию, но в этом случае это необходимо. Если бы мы написали действие как<++*pi
>, оно было бы исполнено немедленно. Это потому, что<++*pi
>не шаблон выражения, а<++*val(pi)
>.
Может быть утомительно обернуть все ваши переменные в<ref()
>и<val()
>в ваши семантические действия. Xpressive предоставляет шаблоны<reference<>
>и<value<>
>, чтобы сделать вещи проще. В следующей таблице показаны эквивалентности:
Table 43.12. reference<> and value<>
Это... |
Это эквивалентно этому... |
---|---|
int i = 0; sregex rex = +( _d [ ++ref(i) ] >> '!' );
|
int i = 0; reference<int> ri(i); sregex rex = +( _d [ ++ri ] >> '!' );
|
<boost::shared_ptr<int>pi(newint(0)); sregexrex=+(_d[++*val(pi)]>>'!');> |
boost::shared_ptr<int> pi(new int(0)); value<boost::shared_ptr<int> > vpi(pi); sregex rex = +( _d [ ++*vpi ] >> '!' );
|
Как видите, при использовании<reference<>
>нужно сначала объявить локальную переменную, а затем объявить ей<reference<>
>. Эти два шага можно объединить в один, используя<local<>
>.
Table 43.13. local<> vs. reference<>
Это... |
Это эквивалентно этому... |
---|---|
<local<int>i(0); sregexrex=+(_d[++i]>>'!');> |
int i = 0; reference<int> ri(i); sregex rex = +( _d [ ++ri ] >> '!' );
|
Мы можем использовать<local<>
>, чтобы переписать приведенный выше пример следующим образом:
local<int> i(0); std::string str("1!2!3?"); // count the exciting digits, but not the // questionable ones. sregex rex = +( _d [ ++i ] >> '!' ); regex_search(str, rex); assert( i.get() == 2 );
Обратите внимание, что мы используем<local<>::get()
>для доступа к значению локальной переменной. Кроме того, остерегайтесь того, что<local<>
>может быть использован для создания висячей ссылки, как<reference<>
>может.
В начале этого раздела мы использовали регекс с семантическим действием, чтобы разобрать строку пар слова/целого числа и вставить их в<std::map<>
>. Это потребовало, чтобы карта и регекс были определены вместе и использовались до того, как они могут выйти из сферы действия. Что, если мы хотим определить регекс один раз и использовать его для заполнения множества различных карт? Мы предпочли бы передать карту в алгоритм<
, а не вставлять ссылку на нее непосредственно в объект регекса. Вместо этого мы можем определить заполнитель места и использовать его в семантическом действии вместо самой карты. Позже, когда мы называем один из алгоритмов регекса, мы можем связать ссылку с реальным объектом карты. Следующий код показывает, как.regex_match()
>
// Define a placeholder for a map object: placeholder<std::map<std::string, int> > _map; // Match a word and an integer, separated by =>, // and then stuff the result into a std::map<> sregex pair = ( (s1= +_w) >> "=>" >> (s2= +_d) ) [ _map[s1] = as<int>(s2) ]; // Match one or more word/integer pairs, separated // by whitespace. sregex rx = pair >> *(+_s >> pair); // The string to parse std::string str("aaa=>1 bbb=>23 ccc=>456"); // Here is the actual map to fill in: std::map<std::string, int> result; // Bind the _map placeholder to the actual map smatch what; what.let( _map = result ); // Execute the match and fill in result map if(regex_match(str, what, rx)) { std::cout << result["aaa"] << '\n'; std::cout << result["bbb"] << '\n'; std::cout << result["ccc"] << '\n'; }
Эта программа показывает:
1 23 456
Здесь мы используем<placeholder<>
>для определения<_map
>, что означает переменную<std::map<>
>. Мы можем использовать заполнитель в семантическом действии, как если бы это была карта. Тогда мы определяем<
структурировать и связывать фактическую карту с заполнителем с помощью «<match_results<>
>.what.let(_map=result);
>». Звонок<
ведет себя так, как если бы заполнитель в семантическом действии был заменен ссылкой на<regex_match()
>result
>.
![]() |
Note |
---|---|
Заполнители в семантических действиях нефактическизаменены во время выполнения ссылками на переменные. Регекс-объект никогда не мутирует ни в одном из алгоритмов, поэтому он безопасен для использования в нескольких потоках. |
Синтаксис для аргументов действия с поздней связью немного отличается, если вы используете<
или<regex_iterator<>
>
. Итераторы регексов принимают дополнительный параметр конструктора для определения связывания аргументов. Существует функция<regex_token_iterator<>
>let()
>, которую можно использовать для связывания переменных с их заполнителями. Следующий код показывает, как.
// Define a placeholder for a map object: placeholder<std::map<std::string, int> > _map; // Match a word and an integer, separated by =>, // and then stuff the result into a std::map<> sregex pair = ( (s1= +_w) >> "=>" >> (s2= +_d) ) [ _map[s1] = as<int>(s2) ]; // The string to parse std::string str("aaa=>1 bbb=>23 ccc=>456"); // Here is the actual map to fill in: std::map<std::string, int> result; // Create a regex_iterator to find all the matches sregex_iterator it(str.begin(), str.end(), pair, let(_map=result)); sregex_iterator end; // step through all the matches, and fill in // the result map while(it != end) ++it; std::cout << result["aaa"] << '\n'; std::cout << result["bbb"] << '\n'; std::cout << result["ccc"] << '\n';
Эта программа показывает:
1 23 456
Вы, вероятно, уже знакомы с утверждениями регулярного выражения. В Perl приведены некоторые примеры утверждений<^
>и<$
>, которые можно использовать для сопоставления начала и конца строки соответственно. Xpressive позволяет вам определить свои собственные утверждения. Обычайное утверждение — это условие, которое должно быть верным в какой-то момент матча, чтобы матч был успешным. Вы можете проверить пользовательское утверждение с помощью функции xpressive<
.check()
>
Есть несколько способов определить таможенное утверждение. Проще всего использовать объект функции. Допустим, вы хотите убедиться, что подэкспрессия соответствует подструне длиной 3 или 6 символов. Следующая структура определяет такой предикат:
// A predicate that is true IFF a sub-match is // either 3 or 6 characters long. struct three_or_six { bool operator()(ssub_match const &sub) const { return sub.length() == 3 || sub.length() == 6; } };
Вы можете использовать этот предикат в обычном выражении следующим образом:
// match words of 3 characters or 6 characters. sregex rx = (bow >> +_w >> eow)[ check(three_or_six()) ] ;
Приведенное выше регулярное выражение будет содержать целые слова длиной 3 или 6 символов. Предикат<three_or_six
>принимает<
, который относится к части строки, соответствующей подвыражению, к которому привязано обычное утверждение.sub_match<>
>
![]() |
Note |
---|---|
Привычное утверждение участвует в определении того, будет ли матч успешным или неудачным. В отличие от действий, которые выполняются лениво, пользовательские утверждения выполняются немедленно, в то время как движок регекса ищет совпадение. |
Пользовательские утверждения также могут быть определены inline с использованием того же синтаксиса, что и для семантических действий. Ниже приведено то же обычное утверждение, написанное в строке:
// match words of 3 characters or 6 characters. sregex rx = (bow >> +_w >> eow)[ check(length(_)==3 || length(_)==6) ] ;
В вышеприведенном<length()
>является ленивой функцией, которая называет<length()
>функцию члена своего аргумента, а<_
>является заполнителем, который получает<sub_match
>.
Как только вы получите возможность писать пользовательские утверждения, они могут быть очень мощными. Например, вы можете написать регулярное выражение, которое соответствует только действительным датам (для некоторого подходящего либерального определения термина& #8220;действительно& #8221;).
int const days_per_month[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 31, 31}; mark_tag month(1), day(2); // find a valid date of the form month/day/year. sregex date = ( // Month must be between 1 and 12 inclusive (month= _d >> !_d) [ check(as<int>(_) >= 1 && as<int>(_) <= 12) ] >> '/' // Day must be between 1 and 31 inclusive >> (day= _d >> !_d) [ check(as<int>(_) >= 1 && as<int>(_) <= 31) ] >> '/' // Only consider years between 1970 and 2038 >> (_d >> _d >> _d >> _d) [ check(as<int>(_) >= 1970 && as<int>(_) <= 2038) ] ) // Ensure the month actually has that many days! [ check( ref(days_per_month)[as<int>(month)-1] >= as<int>(day) ) ] ; smatch what; std::string str("99/99/9999 2/30/2006 2/28/2006"); if(regex_search(str, what, date)) { std::cout << what[0] << std::endl; }
Вышеупомянутая программа распечатывает следующее:
2/28/2006
Обратите внимание, как встроенные пользовательские утверждения используются для проверки значений месяца, дня и года. Регулярное выражение не соответствует<"99/99/9999"
>или<"2/30/2006"
>, поскольку они не являются действительными датами. (Там нет 99-го месяца, а в феврале нет 30 дней.)
Таблицы символов могут быть встроены в регулярную экспрессию только с<std::map<>
>. Ключи карты - это строки, которые необходимо сопоставить, а значения карты - это данные, которые должны быть возвращены в ваше семантическое действие. Хпрессивные атрибуты, названные<a1
>,<a2
>, до<a9
>, удерживают значение, соответствующее соответствующему ключу, чтобы его можно было использовать в семантическом действии. Значение по умолчанию может быть определено для атрибута, если символ не найден.
Впечатляющая таблица символов - это просто<std::map<>
>, где ключ - это тип строки, а значение может быть любым. Например, следующее регулярное выражение соответствует ключу из карты1 и присваивает соответствующее значение атрибуту<a1
>. Затем в семантическом действии он присваивает значение, сохраненное в атрибуте<a1
>, целому результату.
int result; std::map<std::string, int> map1; // ... (fill the map) sregex rx = ( a1 = map1 ) [ ref(result) = a1 ];
Рассмотрим следующий пример кода, который переводит имена чисел в целые числа. Это описано ниже.
#include <string> #include <iostream> #include <boost/xpressive/xpressive.hpp> #include <boost/xpressive/regex_actions.hpp> using namespace boost::xpressive; int main() { std::map<std::string, int> number_map; number_map["one"] = 1; number_map["two"] = 2; number_map["three"] = 3; // Match a string from number_map // and store the integer value in 'result' // if not found, store -1 in 'result' int result = 0; cregex rx = ((a1 = number_map ) | *_) [ ref(result) = (a1 | -1)]; regex_match("three", rx); std::cout << result << '\n'; regex_match("two", rx); std::cout << result << '\n'; regex_match("stuff", rx); std::cout << result << '\n'; return 0; }
Эта программа печатает следующее:
3 2 -1
Сначала программа строит карту чисел с именами номеров в качестве ключей строки и соответствующими целыми числами в качестве значений. Затем он конструирует статическое регулярное выражение, используя атрибут<a1
>для представления результата поиска таблицы символов. В семантическом действии атрибут присваивается целочисленной переменной<result
>. Если символ не найден, то значение по умолчанию<-1
>присваивается<result
>. Дикая карточка<*_
>гарантирует соответствие регекса, даже если символ не найден.
Более полную версию этого примера можно найти в<libs/xpressive/example/numbers.cpp
>. Он переводит имена чисел до «девятьсот девяносто девять миллионов девятьсот девяносто девять тысяч девятьсот девяносто девять» вместе с некоторыми специальными именами чисел, такими как «десятка».
Символические спички по умолчанию чувствительны к регистру, но они могут быть чувствительны к регистру, включив выражение в<icase()
>.
В обычном выражении можно использовать до девяти атрибутов. Они названы<a1
>,<a2
>, ...,<a9
>в пространстве имен<boost::xpressive
>. Тип атрибута такой же, как и второй компонент карты, который ему присваивается. Значение по умолчанию для атрибута может быть определено в семантическом действии с синтаксисом<(a1
|
.default-value
>
Атрибуты должным образом ограничены, поэтому вы можете делать сумасшедшие вещи, такие как:<((a1=sym1)
>>(a1=sym2)[ref(x)=a1])[ref(y)=a1]
>. Внутреннее семантическое действие видит внутреннее<a1
>, а внешнее семантическое действие видит внешнее. Они могут даже иметь разные типы.
![]() |
Note |
---|---|
Xpressive создает скрытую тройку поиска с карты, чтобы она могла быстро искать. Если BOOST_DISABLE_THREADS определена, то скрытая тройка поиска «саморегулируется», поэтому после каждого поиска она реструктурируется для повышения эффективности будущих поисков на основе частоты предыдущих поисков. |
Соответствие обычного выражения строке часто требует информации, зависящей от местоположения. Например, как выполняются бесчувственные сравнения? Локально-чувствительное поведение фиксируется в классе признаков. xpressive предоставляет три шаблона классов черт:<cpp_regex_traits<>
>,<c_regex_traits<>
>и<null_regex_traits<>
>. Первый обертывает a<std::locale
>, второй обертывает глобальную область C, а третий - тип загнутых признаков для использования при поиске нехарактерных данных. Все шаблоны черт соответствуют. Концепция регексных черт.
По умолчанию, xpressive использует<cpp_regex_traits<>
>для всех моделей. Это заставляет все объекты регекса использовать глобальный<std::locale
>. Если вы скомпилируете с<BOOST_XPRESSIVE_USE_C_TRAITS
>определенным, то xpressive будет использовать<c_regex_traits<>
>по умолчанию.
Для создания динамического регекса, использующего пользовательский объект, необходимо использовать<
. Основные этапы показаны в следующем примере:regex_compiler<>
>
// Declare a regex_compiler that uses the global C locale regex_compiler<char const *, c_regex_traits<char> > crxcomp; cregex crx = crxcomp.compile( "\\w+" ); // Declare a regex_compiler that uses a custom std::locale std::locale loc = /* ... create a locale here ... */; regex_compiler<char const *, cpp_regex_traits<char> > cpprxcomp(loc); cregex cpprx = cpprxcomp.compile( "\\w+" );
Объекты<regex_compiler
>действуют как фабрики-регексы. После того, как они были пропитаны локализацией, каждый созданный ими объект регекса будет использовать эту локализацию.
Если вы хотите, чтобы конкретный статический регекс использовал другой набор признаков, вы можете использовать специальный модификатор шаблона<imbue()
>. Например:
// Define a regex that uses the global C locale c_regex_traits<char> ctraits; sregex crx = imbue(ctraits)( +_w ); // Define a regex that uses a customized std::locale std::locale loc = /* ... create a locale here ... */; cpp_regex_traits<char> cpptraits(loc); sregex cpprx1 = imbue(cpptraits)( +_w ); // A shorthand for above sregex cpprx2 = imbue(loc)( +_w );
Модификатор шаблона<imbue()
>должен обернуть весь шаблон. Это ошибка<imbue
>только часть статического регекса. Например:
// ERROR! Cannot imbue() only part of a regex sregex error = _w >> imbue(loc)( _w );
null_regex_traits
С статическими регексами вы не ограничены поиском шаблонов в последовательностях символов. Вы можете искать шаблоны в необработанных байтах, целых числах или в чем-либо, что соответствует. Чар Концепт. В 1546 г. это было просто. Это реализация концепции Regex Traits (1549). Он не распознает классы символов и не делает чувствительных к случаю отображений.
Например, с<null_regex_traits<>
>можно написать статический регекс, чтобы найти рисунок в последовательности целых чисел следующим образом:
// some integral data to search int const data[] = {0, 1, 2, 3, 4, 5, 6}; // create a null_regex_traits<> object for searching integers ... null_regex_traits<int> nul; // imbue a regex object with the null_regex_traits ... basic_regex<int const *> rex = imbue(nul)(1 >> +((set= 2,3) | 4) >> 5); match_results<int const *> what; // search for the pattern in the array of integers ... regex_search(data, data + 7, what, rex); assert(what[0].matched); assert(*what[0].first == 1); assert(*what[0].second == 6);
Сжимайте максимальную производительность из-за этих советов и трюков.
Составление регекса (динамического или статического)далекодороже, чем выполнение матча или поиска. Если у вас есть возможность, предпочтите компилировать шаблон в<
возражать один раз и использовать его повторно, а не воссоздавать снова и снова.basic_regex<>
>.
Поскольку объекты<
не мутируются ни одним из алгоритмов регекса, они полностью защищены от потоков после завершения их инициализации (и любой грамматики, членами которой они являются). Самый простой способ повторного использования ваших шаблонов — это просто сделать ваш<basic_regex<>
>
Объекты «статического конста».basic_regex<>
>.
Объект<
кэширует динамически выделенную память. По этой причине гораздо лучше повторно использовать один и тот же объект<match_results<>
>
, если вам нужно выполнить много поисковых запросов.match_results<>
>
Пещера:<
объекты не являются нитями-безопасными, поэтому не используйте их повторно.match_results<>
>
Это является следствием предыдущего совета. Если вы выполняете несколько поисков, вы должны предпочесть алгоритмы регекса, которые принимают объект<
, а не те, которые этого не делают, и вы должны повторно использовать тот же<match_results<>
>
каждый раз. Если вы не предоставляете<match_results<>
>.
объект, временный будет создан для вас и отброшен, когда алгоритм вернется. Любая память, кэшированная в объекте, будет размещена и должна быть перераспределена в следующий раз.match_results<>
>
xpressive обеспечивает перегрузки алгоритмов<
и<regex_match()
>
, которые работают на нулевых строках в стиле C. Вы должны предпочесть перегрузки, которые принимают диапазоны итераторов. Когда вы передаете нулевую строку алгоритму регекса, конечный итератор вычисляется немедленно по телефону<regex_search()
>strlen
>. Если вы уже знаете длину строки, вы можете избежать этих накладных расходов, позвонив в алгоритмы регекса с парой<[begin,end)
>.
В среднем статические регексы выполняются на 10-15% быстрее, чем их динамические аналоги. Стоит ознакомиться со статическим диалектом регекса.
syntax_option_type::optimize
Флаг<optimize
>сообщает компилятору regex, чтобы он потратил дополнительное время на анализ шаблона. Это может привести к тому, что некоторые шаблоны будут выполняться быстрее, но это увеличивает время на компиляцию шаблона и часто увеличивает количество памяти, потребляемой шаблоном. Если вы планируете повторно использовать свой шаблон,<optimize
>обычно является выигрышем. Если вы используете шаблон только один раз, не используйте<optimize
>.
Имейте в виду следующие советы, чтобы избежать наступления в выбоинах с депрессией.
С помощью статических регексов вы можете создавать грамматики, вставляя регексы друг в друга. При составлении внешнего регекса изменяются как внешние, так и внутренние объекты регекса, а также все объекты регекса, к которым они относятся прямо или косвенно. По этой причине для глобальных объектов регекса опасно участвовать в грамматике. Лучше всего создавать грамматику регекса из одной нити. После создания результирующая грамматика regex может быть выполнена из нескольких потоков без проблем.
Это ловушка, общая для многих двигателей регулярного выражения. Некоторые модели могут вызвать экспоненциально плохую производительность. Часто эти паттерны включают в себя один количественный термин, вложенный в другой квантор, такой как<"(a*)*"
>, хотя во многих случаях проблему труднее обнаружить. Остерегайтесь шаблонов, которые имеют вложенные количественные показатели.
Если тип<BidiIterT
>используется в качестве шаблонного аргумента<
, то<basic_regex<>
>CharT
>является<iterator_traits<BidiIterT>::value_type
>. Тип<CharT
>должен иметь тривиальный конструктор по умолчанию, конструктор копий, оператор назначения и деструктор. Кроме того, для объектов должны быть выполнены следующие требования:<c
>типа<CharT
>,<c1
>и<c2
>типа<CharTconst
>и<i
>типа<int
>:
Table 43.14. CharT Requirements
Выражение |
Тип возврата |
Утверждение/Примечание/Предварительное/Пост-условие |
---|---|---|
|
|
Default constructor (must be trivial). |
< |
|
Copy constructor (must be trivial). |
< |
|
Назначение оператора (должно быть тривиальным). |
< |
< |
|
< |
< |
|
< |
< |
|
|
< |
< |
< |
< |
< |
< |
< |
< |
|
< |
|
|
|
< |
В следующей таблице<X
>обозначает класс признаков, определяющий типы и функции для типа контейнера символов<CharT
>;<u
>является объектом типа<X
>;<v
>является объектом типа<constX
>;<p
>является значением типа<constCharT*
>;<I1
>и<I2
>являются<InputIterators
>;<c
>является значением типа<constCharT
>;<s
>является объектом типа<X::string_type
>;<cs
>является объектом типа<constX::string_type
>;<b
>является значением типа<bool
>;<i
>является значением типа<int
>;<F1
>и<F2
>являются значениями типа<constCharT*
>;<loc
>является объектом типа<X::locale_type
>; и<ch
>является объектом типа<constchar
>.
Table 43.15. Traits Requirements
Выражение |
Тип возврата |
Утверждение/Примечание |
---|---|---|
|
|
The character container type used in the implementation of class
template |
|
< |
|
< |
Implementation defined |
A copy constructible type that represents the locale used by the traits class. |
|
Implementation defined |
A bitmask type representing a particular character classification. Multiple values of this type can be bitwise-or'ed together to obtain a new valid value. |
< |
< |
Приносит значение между< |
|
|
Widens the specified |
< |
< |
For any characters |
|
< |
Для персонажей< |
|
|
Возвращает символ так, что для любого персонажа< |
< |
|
Для всех знаков< |
|
|
Returns a sort key for the character sequence designated by the
iterator range |
< |
|
Returns a sort key for the character sequence designated by the
iterator range |
|
|
Преобразует последовательность символов, обозначенную диапазоном итераторов< |
|
|
Возвращает последовательность символов, которая представляет коллатинговый элемент, состоящий из последовательности символов, обозначенной диапазоном итераторов< |
|
< |
Returns |
|
< |
Returns the value represented by the digit |
|
< |
Имбуи< |
|
< |
Возвращает текущее местоположение, используемое< |
Этот раздел адаптирован с эквивалентной страницы в документацииBoost.Regexи с предложениядля добавления регулярных выражений в Стандартную библиотеку.
Ниже вы можете найти шесть полных образцов.
Вот пример из введения. Он воспроизводится здесь для вашего удобства.
#include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost::xpressive; int main() { std::string hello( "hello world!" ); sregex rex = sregex::compile( "(\\w+) (\\w+)!" ); smatch what; if( regex_match( hello, what, rex ) ) { std::cout << what[0] << '\n'; // whole match std::cout << what[1] << '\n'; // first capture std::cout << what[2] << '\n'; // second capture } return 0; }
Эта программа предусматривает следующее:
hello world! hello world
Обратите внимание в этом примере, как мы используем пользовательские<mark_tag
>s, чтобы сделать шаблон более читаемым. Мы можем использовать<mark_tag
>s позже, чтобы проиндексировать в<
.match_results<>
>
#include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost::xpressive; int main() { char const *str = "I was born on 5/30/1973 at 7am."; // define some custom mark_tags with names more meaningful than s1, s2, etc. mark_tag day(1), month(2), year(3), delim(4); // this regex finds a date cregex date = (month= repeat<1,2>(_d)) // find the month ... >> (delim= (set= '/','-')) // followed by a delimiter ... >> (day= repeat<1,2>(_d)) >> delim // and a day followed by the same delimiter ... >> (year= repeat<1,2>(_d >> _d)); // and the year. cmatch what; if( regex_search( str, what, date ) ) { std::cout << what[0] << '\n'; // whole match std::cout << what[day] << '\n'; // the day std::cout << what[month] << '\n'; // the month std::cout << what[year] << '\n'; // the year std::cout << what[delim] << '\n'; // the delimiter } return 0; }
Эта программа предусматривает следующее:
5/30/1973 30 5 1973 /
Следующая программа находит даты в строке и отмечает их псевдо-HTML.
#include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost::xpressive; int main() { std::string str( "I was born on 5/30/1973 at 7am." ); // essentially the same regex as in the previous example, but using a dynamic regex sregex date = sregex::compile( "(\\d{1,2})([/-])(\\d{1,2})\\2((?:\\d{2}){1,2})" ); // As in Perl, $& is a reference to the sub-string that matched the regex std::string format( "<date>$&</date>" ); str = regex_replace( str, date, format ); std::cout << str << '\n'; return 0; }
Эта программа предусматривает следующее:
I was born on <date>5/30/1973</date> at 7am.
Следующая программа находит слова в строке с широкими символами. Используется<wsregex_iterator
>. Заметьте, что ссылка на<wsregex_iterator
>дает объект<wsmatch
>.
#include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost::xpressive; int main() { std::wstring str( L"This is his face." ); // find a whole word wsregex token = +alnum; wsregex_iterator cur( str.begin(), str.end(), token ); wsregex_iterator end; for( ; cur != end; ++cur ) { wsmatch const &what = *cur; std::wcout << what[0] << L'\n'; } return 0; }
Эта программа предусматривает следующее:
This is his face
Следующая программа находит время гонки в строке и отображает сначала минуты, а затем секунды. Используется<
.regex_token_iterator<>
>
#include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost::xpressive; int main() { std::string str( "Eric: 4:40, Karl: 3:35, Francesca: 2:32" ); // find a race time sregex time = sregex::compile( "(\\d):(\\d\\d)" ); // for each match, the token iterator should first take the value of // the first marked sub-expression followed by the value of the second // marked sub-expression int const subs[] = { 1, 2 }; sregex_token_iterator cur( str.begin(), str.end(), time, subs ); sregex_token_iterator end; for( ; cur != end; ++cur ) { std::cout << *cur << '\n'; } return 0; }
Эта программа предусматривает следующее:
4 40 3 35 2 32
Следующая программа берет некоторый текст, который был отмечен html, и удаляет разметку. Он использует регекс, который соответствует тегу HTML и<
, который возвращает части строки, которые соответствуют регексу, а не.regex_token_iterator<>
>
#include <iostream> #include <boost/xpressive/xpressive.hpp> using namespace boost::xpressive; int main() { std::string str( "Now <bold>is the time <i>for all good men</i> to come to the aid of their</bold> country." ); // find a HTML tag sregex html = '<' >> optional('/') >> +_w >> '>'; // the -1 below directs the token iterator to display the parts of // the string that did NOT match the regular expression. sregex_token_iterator cur( str.begin(), str.end(), html, -1 ); sregex_token_iterator end; for( ; cur != end; ++cur ) { std::cout << '{' << *cur << '}'; } std::cout << '\n'; return 0; }
Эта программа предусматривает следующее:
{Now }{is the time }{for all good men}{ to come to the aid of their}{ country.}
Вот класс помощников, чтобы продемонстрировать, как вы можете отображать дерево вложенных результатов:
// Displays nested results to std::cout with indenting struct output_nested_results { int tabs_; output_nested_results( int tabs = 0 ) : tabs_( tabs ) { } template< typename BidiIterT > void operator ()( match_results< BidiIterT > const &what ) const { // first, do some indenting typedef typename std::iterator_traits< BidiIterT >::value_type char_type; char_type space_ch = char_type(' '); std::fill_n( std::ostream_iterator<char_type>( std::cout ), tabs_ * 4, space_ch ); // output the match std::cout << what[0] << '\n'; // output any nested matches std::for_each( what.nested_results().begin(), what.nested_results().end(), output_nested_results( tabs_ + 1 ) ); } };
Статья User's Guide раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 43. Boost.Xpressive может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 43. Boost.Xpressive ::
реклама |