Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

User's Guide

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 43. Boost.Xpressive

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

В этом разделе описывается, как использовать xpressive для выполнения текстовых манипуляций и разбора задач. Если вы ищете подробную информацию о конкретных компонентах в xpressive, проверьте разделСсылка.

What is xpressive?

xpressive - это библиотека шаблонов регулярного выражения. Регулярные выражения (regexes) могут быть записаны как строки, которые динамически анализируются во время выполнения (динамические regexes), или какшаблоны выражения., которые разбираются по времени компиляции (статические регексы). Динамические регексы имеют то преимущество, что их можно принимать от пользователя в качестве входных данных во время выполнения или считывать из файла инициализации. Статические регексы имеют несколько преимуществ. Поскольку они являются выражениями C++ вместо строк, они могут быть проверены синтаксисом во время компиляции. Кроме того, они могут, естественно, ссылаться на код и данные в другом месте вашей программы, давая вам возможность перезвонить в ваш код из матча регекс. Наконец, поскольку они статически связаны, компилятор может генерировать более быстрый код для статических регексов.

Двойственная природа депрессии уникальна и сильна. Статический прессинг немного похож наSpirit Parser Framework. Как иSpirit, вы можете создавать грамматики со статическими регексами, используя шаблоны выражения. (В отличие отSpirit, Xpressive делает исчерпывающее отступление, пытаясь найти совпадение для вашего шаблона.) Динамический xpressive немного похож наBoost.Regex. На самом деле, интерфейс xpressive должен быть знаком любому, кто использовалBoost.Regex. инновация xpressive заключается в том, что позволяет смешивать и сопоставлять статические и динамические регексы в одной программе и даже в одном выражении! Вы можете встроить динамический регекс в статический регекс илинаоборот, и встроенный регекс будет полностью участвовать в поиске, отслеживая задний ход по мере необходимости, чтобы сделать матч успешным.

Hello, world!

Достаточно теории. Давайте посмотримЗдравствуй, Мир, стиль прессинга:

#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

Первое, что вы заметите о коде, это то, что все типы в xpressive живут в пространстве имен<boost::xpressive>.

[Note] Note

Большинство остальных примеров в этом документе оставят без внимания директиву<usingnamespace boost::xpressive;>. Просто притворись, что он там.

Далее вы заметите тип объекта регулярного выражения<sregex>. Если вы знакомы сBoost.Regex, это отличается от того, к чему вы привыкли. «<s>» в «<sregex>» означает «<string>», что указывает на то, что этот регекс может использоваться для нахождения паттернов в<std::string>объектах. Я подробно обсудю эту разницу и ее последствия позже.

Обратите внимание, как инициализируется объект regex:

sregex rex = sregex::compile( "(\\w+) (\\w+)!" );

Для создания объекта регулярного выражения из струны необходимо назвать фабричный метод, такой как<basic_regex<>::compile()>. Это еще одна область, в которой xpressive отличается от других объектно-ориентированных библиотек регулярных выражений. Другие библиотеки поощряют вас думать о регулярном выражении как о своеобразной веревке на стероидах. В депрессивном состоянии регулярные выражения не являются струнами; они представляют собой небольшие программы на доменном языке. Стринги являются лишь однимпредставлениемэтого языка. Другим представлением является шаблон выражения. Например, приведенная выше строка кода эквивалентна следующему:

sregex rex = (s1= +_w) >> ' ' >> (s2= +_w) >> '!';

Это описывает то же самое регулярное выражение, за исключением того, что оно использует доменно-специфический встроенный язык, определяемый статическим xpressive.

Как видите, статические регексы имеют синтаксис, заметно отличающийся от стандартного синтаксиса Perl. Это связано с тем, что мы ограничены синтаксисом C++. Самое большое различие заключается в том, что<>>>означает «последующий». Например, в Perl можно просто поставить суб-выражения рядом друг с другом:

abc

Но в C++ должен быть оператор, разделяющий подвыражения:

a >> b >> c

В Перле скобки<()>имеют особое значение. Они группируются, но как побочный эффект они также создают обратные ссылки, такие как<$1>и<$2>. В C++ нет способа перегрузить скобки, чтобы дать им побочные эффекты. Чтобы получить тот же эффект, мы используем специальные<s1>,<s2>и т. Д. Токены. Назначьте один, чтобы создать обратную ссылку (известную как подматч в xpressive).

Вы также заметите, что оператор повторения<+>переместился из позиции постфикса в позицию префикса. Это потому, что у C++ нет оператора постфикса<+>. Итак:

"\\w+"

Это то же самое, что:

+_w

Мы рассмотрим все остальные различияпозже.

Getting xpressive

Есть два способа стать депрессивным. Первое и самое простое — скачать последнюю версию Boost. Просто перейдите наhttp://sf.net/projects/boostи перейдите по ссылке& #8220;Download& #8221;.

Второй способ — прямой доступ к репозиторию Boost Subversion. Просто перейдите наhttp://svn.boost.org/trac/boost/и следовать инструкциям для анонимного доступа к Subversion. Версия в Boost Subversion нестабильна.

Building with xpressive

Xpressive - это библиотека шаблонов только для заголовков, что означает, что вам не нужно изменять скрипты сборки или ссылаться на какой-либо отдельный файл lib, чтобы использовать его. Все, что вам нужно сделать, это<#include<boost/xpressive/xpressive.hpp>>. Если вы используете только статические регексы, вы можете улучшить время компиляции, только включив<xpressive_static.hpp>. Вы также можете включить<xpressive_dynamic.hpp>, если вы планируете использовать только динамические регексы.

Если вы также хотите использовать семантические действия или пользовательские утверждения со своими статическими регексами, вам необходимо дополнительно включить<regex_actions.hpp>.

Requirements

Для Xpressive требуется версия 1.34.1 или выше.

Supported Compilers

В настоящее время, Boost. Известно, что Xpressive работает на следующих компиляторах:

  • Visual C++ 7.1 и выше
  • GNU C++ 3.4 и выше
  • Intel для Linux 8.1 и выше
  • Intel для Windows 10 и выше
  • tru64cxx 71 и выше
  • MinGW 3.4 и выше
  • HP C/aC++ A.06.14

Ознакомьтесь с последними результатами тестов на странице результатов регрессии Boost.

[Note] Note

Пожалуйста, отправьте любые вопросы, комментарии и отчеты об ошибках в ericboost-consultingcom.

Вам не нужно много знать, чтобы начать продуктивно работать с депрессией. Начнем с никелевого тура по типам и алгоритмам, которые предоставляет xpressive.

Table 43.1. xpressive's Tool-Box

Инструмент

Описание

<basic_regex<>>

Contains a compiled regular expression. basic_regex<> is the most important type in xpressive. Everything you do with xpressive will begin with creating an object of type basic_regex<>.

match_results<>, sub_match<>

<match_results<>>содержит результаты<regex_match()>или<regex_search()>операции. Он действует как вектор<sub_match<>>объектов. Объект<sub_match<>>содержит помеченную субэкспрессию (также известную как обратная ссылка в Perl). В основном это просто пара итераторов, представляющих начало и конец отмеченного суб-выражения.

regex_match()

Проверяет, соответствует ли строка регексу. Чтобы<regex_match()>преуспеть,вся струнадолжна соответствовать регексу от начала до конца. Если вы дадите<regex_match()><match_results<>>, он запишет в него любые отмеченные субвыражения, которые он найдет.

regex_search()

Searches a string to find a sub-string that matches the regex. regex_search() will try to find a match at every position in the string, starting at the beginning, and stopping when it finds a match or when the string is exhausted. As with regex_match(), if you give regex_search() a match_results<>, it will write into it any marked sub-expressions it finds.

regex_replace()

Given an input string, a regex, and a substitution string, regex_replace() builds a new string by replacing those parts of the input string that match the regex with the substitution string. The substitution string can contain references to marked sub-expressions.

regex_iterator<>

An STL-compatible iterator that makes it easy to find all the places in a string that match a regex. Dereferencing a regex_iterator<> returns a match_results<>. Incrementing a regex_iterator<> finds the next match.

regex_token_iterator<>

Like regex_iterator<>, except dereferencing a regex_token_iterator<> returns a string. By default, it will return the whole sub-string that the regex matched, but it can be configured to return any or all of the marked sub-expressions one at a time, or even the parts of the string that didn't match the regex.

<regex_compiler<>>

A factory for basic_regex<> objects. It "compiles" a string into a regular expression. You will not usually have to deal directly with regex_compiler<> because the basic_regex<> class has a factory method that uses regex_compiler<> internally. But if you need to do anything fancy like create a basic_regex<> object with a different std::locale, you will need to use a regex_compiler<> explicitly.


Теперь, когда вы немного знаете об инструментах, вы можете выбрать правильный инструмент для вас, ответив на следующие два вопроса:

  1. Какой типитераторавы будете использовать для обхода ваших данных?
  2. Что вы хотитесделатьс вашими данными?

Know Your Iterator Type

Большинство классов в xpressive - это шаблоны, которые параметризованы по типу итератора. Xpressive определяет некоторые общие типдефы, чтобы облегчить выбор правильных типов. Вы можете использовать таблицу ниже, чтобы найти правильные типы в зависимости от типа вашего итератора.

Table 43.2. xpressive Typedefs vs. Iterator Types

std::string::const_iterator

char const *

std::wstring::const_iterator

char_t const *

<basic_regex<>>

sregex

<cregex>

<wsregex>

<wcregex>

match_results<>

<smatch>

cmatch

wsmatch

wcmatch

<regex_compiler<>>

sregex_compiler

cregex_compiler

<wsregex_compiler>

<wcregex_compiler>

regex_iterator<>

<sregex_iterator>

<cregex_iterator>

wsregex_iterator

wcregex_iterator

regex_token_iterator<>

<sregex_token_iterator>

<cregex_token_iterator>

<wsregex_token_iterator>

wcregex_token_iterator


Вы должны заметить, что систематическая конвенция именования. Многие из этих типов используются вместе, поэтому соглашение об именах помогает вам использовать их последовательно. Например, если у вас есть<sregex>, вы также должны использовать<smatch>.

Если вы не используете один из этих четырех типов итераторов, вы можете использовать шаблоны напрямую и указать тип итератора.

Know Your Task

Вы хотите найти шаблон один раз? Много раз? Поиск и замена? У хладнокровия есть инструменты для всего этого и многого другого. Ниже приведена краткая ссылка:


Эти алгоритмы и классы подробно описаны в справочном разделе.

[Tip] Tip

Попробуйте нажать на задачу в таблице выше, чтобы увидеть полную примерную программу, которая использует xpressive для решения этой конкретной задачи.

Первое, что вы сделаете при использовании xpressive, это создадите объект<basic_regex<>>. В этом разделе рассматриваются гайки и болты построения регулярного выражения на двух диалектах: статичной и динамичной.

Overview

Особенностью, которая действительно отличает xpressive от других библиотек регулярных выражений C/C++, является возможность создания регулярных выражений с использованием выражений C++. xpressive достигает этого за счет перегрузки оператора, используя метод, называемыйшаблонами выражения, для встраивания мини-языка, посвященного сопоставлению шаблонов в C++. Эти «статические регексы» имеют много преимуществ перед своими струнными собратьями. В частности, статические регексы:

  • Они проверяются синтаксисом во время компиляции; они никогда не потерпят неудачу во время выполнения из-за ошибки синтаксиса.
  • Естественно, можно ссылаться на другие данные и код C++, в том числе на другие регексы, что упрощает создание грамматики из регулярных выражений и связывает определенные пользователем действия, которые выполняются, когда части вашего регекса совпадают.
  • Они статически связаны для лучшего настроя и оптимизации. Статические регексы не требуют таблиц состояний, виртуальных функций, байт-кода или вызовов через указатели функций, которые не могут быть решены во время компиляции.
  • Они не ограничиваются поиском шаблонов в строках. Вы можете объявить статический регекс, который находит шаблоны в массиве целых чисел, например.

Поскольку мы составляем статические регексы с использованием выражений C++, мы ограничены правилами для легальных выражений C++. К сожалению, это означает, что «классический» синтаксис регулярных выражений не всегда может быть четко отображен на C++. Скорее, мы сопоставляем регекс, выбирая новый синтаксис, который является законным C++.

Construction and Assignment

Вы создаете статический регекс, назначая его объекту типа<basic_regex<>>. Например, следующее определяет регекс, который может быть использован для поиска закономерностей в объектах типа<std::string>:

sregex re = '$' >> +_d >> '.' >> _d >> _d;

Назначение работает аналогично.

Character and String Literals

В статических регексах буквалы символов и струн совпадают сами с собой. Например, в приведенном выше регексе<'$'>и<'.'>соответствуют символам<'$'>и<'.'>соответственно. Не следует путать с тем, что<$>и<.>являются мета-характерами в 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

Sequencing and Alternation

Как вы, наверное, уже заметили, суб-выражения в статических регексах должны быть разделены оператором секвенирования<>>>. Вы можете прочитать этого оператора как «следующий».

// 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 );

Grouping and Captures

В Perl скобки<()>имеют особое значение. Они группируются, но как побочный эффект они также создают обратные ссылки, такие как<$1>и<$2>. В C++ скобки только групповые — нет способа дать им побочные эффекты. Чтобы получить тот же эффект, мы используем специальные токены<s1>,<s2>и т. Д. Присвоение одному создает обратную ссылку. Затем вы можете использовать обратную ссылку в своем выражении, например, используя<\1>и<\2>в Perl. Например, рассмотрим следующий регекс, который находит соответствующие HTML-теги:

"<(\\w+)>.*?</\\1>"

В статической депрессии это будет:

'<' >> (s1= +_w) >> '>' >> -*_ >> "</" >> s1 >> '>'

Обратите внимание, как вы захватываете обратную ссылку, назначая<s1>, а затем используете<s1>позже в шаблоне, чтобы найти соответствующий конечный тег.

[Tip] Tip

Группировка без задней ссылки

Если вы просто хотите сгруппироваться без обратной ссылки, вы можете просто использовать<()>без<s1>. Это эквивалентно конструкции группировки Перла<(?:)>без захвата.

Case-Insensitivity and Internationalization

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>. См. разделЛокализация и характеристики регексовдля получения дополнительной информации о том, как настроить поведение ваших регексов.

Static xpressive Syntax Cheat Sheet

В приведенной ниже таблице перечислены знакомые конструкции регекса и их эквиваленты в статической депрессии.

Table 43.4. Perl syntax vs. Static xpressive syntax

Перл

Статический xpressive

значение

<.>

<_>

любой символ (при условии модификатора Perl/s).

<ab>

<a>> b>

секвенирование<a>и<b>подвыражения.

<a|b>

<a| b>

чередование<a>и<b>подвыражения.

<(a)>

<(s1=a)>

сгруппировать и получить обратную ссылку.

<(?:a)>

<(a)>

группировать и не фиксировать обратную ссылку.

<\1>

<s1>

ранее полученная обратная ссылка.

<a*>

<*a>

ноль или более раз, жадный.

<a+>

<+a>

Один или несколько раз, жадный.

<a?>

<!a>

ноль или один раз, жадный.

<a{n,m}>

<repeat<n,m>(a)>

между<n>и<m>временами, жадные.

<a*?>

<-*a>

ноль или более раз, не жадный.

<a+?>

<-+a>

один или несколько раз, не жадный.

<a??>

<-!a>

ноль или один раз, не жадный.

<a{n,m}?>

<-repeat<n,m>(a)>

между<n>и<m>разами, без жадности.

<^>

<bos>

начало утверждения последовательности.

<$>

<eos>

конец утверждения последовательности.

<\b>

<_b>

Утверждение границы слова.

<\B>

<~_b>

не является утверждением границ.

<\n>

<_n>

Буквальная новая линия.

<.>

<~_n>

любой символ, кроме буквальной новизны (без модификатора Perl/s).

<\r?\n|\r>

<_ln>

Новая логическая линия.

<[^\r\n]>

<~_ln>

любой отдельный символ не является логической новой линией.

<\w>

<_w>

символ слова, эквивалентный множеству [alnum | '_'].

<\W>

<~_w>

не является символом слова, эквивалентным ~set [alnum | '_'].

<\d>

<_d>

Цифровой знак.

<\D>

<~_d>

не является цифрой.

<\s>

<_s>

Пространственный характер.

<\S>

<~_s>

Не является космическим персонажем.

<[:alnum:]>

<alnum>

— буквенно-цифровой знак.

<[:alpha:]>

<alpha>

Азбука.

<[:blank:]>

<blank>

Горизонтальный символ белого пространства.

<[:cntrl:]>

<cntrl>

Управляющий персонаж.

<[:digit:]>

<digit>

Цифровой знак.

<[:graph:]>

<graph>

Графический персонаж.

<[:lower:]>

<lower>

Нижний регистр.

<[:print:]>

<print>

Печатный знак.

<[:punct:]>

<punct>

— знак препинания.

<[:space:]>

<space>

Белое пространство.

<[:upper:]>

<upper>

Верхний регистр.

<[:xdigit:]>

<xdigit>

шестнадцатеричная цифра.

<[0-9]>

<range('0','9')>

символы в диапазоне<'0'>-<'9'>.

<[abc]>

<as_xpr('a')|'b'|'c'>

символов<'a'>,<'b'>или<'c'>

.

<[abc]>

<(set='a','b','c')>

то же, что и выше

<[0-9abc]>

<set[range('0','9')| 'a'| 'b'| 'c']>

символов<'a'>,<'b'>,<'c'>или в диапазоне от<'0'>до<'9'>.

<[0-9abc]>

<set[range('0','9')| (set='a','b','c')]>

то же, что и выше

<[^abc]>

<~(set='a','b','c')>

не символы<'a'>,<'b'>или<'c'>

.

<(?i:stuff)>

<icase(><stuff><)>

матчвещине принимая во внимание случай.

<(?>stuff)>

<keep(><stuff><)>

независимое подвыражение, совпадениематериалаи выключение обратного отсчета.

<(?=stuff)>

<before(><stuff><)>

позитивный взгляд вперед утверждение, матч если раньшевещи, но не включатьвещив матче.

<(?!stuff)>

<~before(><stuff><)>

отрицательный взгляд вперед утверждение, совпадение если не раньшематериал.

<(?<=stuff)>

<after(><stuff><)>

позитивный взгляд за утверждением, матч послевещи, но не включатьвещив матче.материалдолжен иметь постоянную ширину.]

<(?<!stuff)>

<~after(><stuff><)>

отрицательный взгляд за утверждением, совпадение если не послевещи.вещидолжны быть постоянной шириной.]

<(?P<name>stuff)>

<mark_tag><name><(>n<);>
...
<(><name><=><stuff><)>

Создать именованный захват.

<(?P=name)>

<mark_tag><name><(>n<);>
...
<name>

Возвращайтесь к ранее созданному названию захвата.



Overview

Статические регексы денди, но иногда вам нужно что-то более динамичное. Представьте, что вы разрабатываете текстовый редактор с функцией поиска / замены регекса. Вы должны принять регулярное выражение от конечного пользователя в качестве ввода во время выполнения. Должен быть способ разобрать строку в обычное выражение. Для этого и нужны динамические регексы. Они построены из тех же основных компонентов, что и их статические аналоги, но они запаздывают, поэтому вы можете указать их во время выполнения.

Construction and Assignment

Существует два способа создания динамического регекса: с функцией<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<>>.

Dynamic xpressive Syntax

Поскольку динамический синтаксис не ограничен правилами для корректных выражений C++, мы можем свободно использовать знакомый синтаксис для динамических регексов. По этой причине синтаксис, используемый для динамических регексов, следует примеру, предложенному Джоном Мэддоком, чтобы добавить регулярные выражения в Стандартную библиотеку. По сути, это синтаксис, стандартизированныйECMAScript, с незначительными изменениями в поддержке интернационализации.

Поскольку синтаксис полностью документирован в другом месте, я просто отсылаю вас к существующим стандартам, а не дублирую спецификацию здесь.

Internationalization

Как и в случае с статическими регексами, динамические регексы поддерживают интернационализацию, позволяя указать другой<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">.

Overview

После создания объекта регекса можно использовать алгоритмы<regex_match()>и<regex_search()>для поиска шаблонов в строках. Эта страница охватывает основы сопоставления и поиска регексов. Во всех случаях, если вы знакомы с тем, как работают<regex_match()>и<regex_search()>в библиотекеBoost.Regex, версии xpressive работают одинаково.

Seeing if a String Matches a Regex

Алгоритм<regex_match()>проверяет, соответствует ли регекс заданному входу.

[Warning] Warning

Алгоритм<regex_match()>будет сообщать об успехе только в том случае, если регекс соответствуетвсему входуот начала до конца. Если регекс соответствует только части входа,<regex_match()>вернётся ложным. Если вы хотите выполнить поиск по строке в поисках подстрок, соответствующих регексу, используйте алгоритм<regex_search()>.

Вход может представлять собой двунаправленный диапазон, такой как<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()>, чтобы увидеть полный список доступных перегрузок.

Searching for Matching Sub-Strings

Используйте<regex_search()>, когда вы хотите знать, содержит ли входная последовательность подпоследовательность, которая соответствует регексу.<regex_search()>будет пытаться сопоставить регекс в начале входной последовательности и сканировать вперед в последовательности, пока он либо не найдет совпадение, либо не исчерпает последовательность.

Во всех других отношениях<regex_search()>ведет себя как<regex_match()>(см. выше). В частности, он может работать на двунаправленном диапазоне, таком как<std::string>, нулевые струны в стиле C или диапазоны итераторов. Необходимо также позаботиться о том, чтобы тип итератора вашего регекса соответствовал типу итератора вашей входной последовательности. Как и в случае с<regex_match()>, вы можете дополнительно предоставить<match_results<>>структуру для получения результатов поиска и<match_flag_type>битмаску для управления оценкой соответствия.

Нажмитездесь, чтобы увидеть полную примерную программу, которая показывает, как использовать<regex_search()>. И проверьте ссылку<regex_search()>, чтобы увидеть полный список доступных перегрузок.

Overview

Иногда недостаточно просто знать, был ли успех<regex_match()>или<regex_search()>. Если вы передадите объект типа<match_results<>><regex_match()>или<regex_search()>, то после успешного завершения алгоритма<match_results<>>будет содержать дополнительную информацию о том, какие части регекса совпадали с какими частями последовательности. В Perl эти подпоследовательности называютсяобратными ссылками, и они хранятся в переменных<$1>,<$2>и т.д. В xpressive они являются объектами типа<sub_match<>>, и они хранятся в структуре<match_results<>>, которая действует как вектор<sub_match<>>объектов.

match_results

Итак, вы передали объект<match_results<>>алгоритму регекса, и алгоритм преуспел. Теперь вы хотите изучить результаты. Большая часть того, что вы будете делать с объектом<match_results<>>, индексируется в него, чтобы получить доступ к его внутренним объектам<sub_match<>>, но есть несколько других вещей, которые вы можете сделать с объектом<match_results<>>.

В таблице ниже показано, как получить доступ к информации, хранящейся в объекте<match_results<>>под названием<what>.

Table 43.5. match_results<> Accessors

Аксессуар

последствия

<what.size()>

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.

what[n]

Returns the n-th sub-match.

what.length(n)

Returns the length of the n-th sub-match. Same as what[n].length().

what.position(n)

Returns the offset into the input sequence at which the n-th sub-match begins.

what.str(n)

Returns a std::basic_string<> constructed from the n-th sub-match. Same as what[n].str().

what.prefix()

Возвращает<sub_match<>>объект, который представляет подпоследовательность от начала входной последовательности до начала полного соответствия.

what.suffix()

Returns a sub_match<> object which represents the sub-sequence from the end of the full match to the end of the input sequence.

what.regex_id()

Returns the regex_id of the basic_regex<> object that was last used with this match_results<> object.


Вы можете сделать больше с объектом<match_results<>>, но это будет покрыто, когда мы говорим ограмматиках и вложенных матчах.

sub_match

Когда вы индексируете<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

Аксессуар

последствия

<sub.length()>

Returns the length of the sub-match. Same as std::distance(sub.first,sub.second).

sub.str()

Возвращает<std::basic_string<>>построенный из подматча. Так же, как<std::basic_string<char_type>(sub.first,sub.second)>

.

sub.compare(str)

Выполняет сравнение струн между подматчем и<str>, где<str>может быть<std::basic_string<>>, C-стилем нулевой струны или другим подматчем. Так же, как<sub.str().compare(str)>

.

caution Results Invalidation caution

Результаты хранятся в виде итераторов во входной последовательности. Все, что отменяет входную последовательность, отменяет результаты матча. Например, если вы соответствуете объекту<std::string>, результаты действительны только до следующего вызова функции неконст-члена этого объекта<std::string>. После этого результаты, проведенные объектом<match_results<>>, недействительны. Не используй их!

Регулярные выражения хороши не только для поиска текста, но и для манипулирования им. Одной из наиболее распространенных задач манипулирования текстом является поиск и замена. xpressive предоставляет алгоритм поиска и замены<regex_replace()>.

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()>, чтобы увидеть полный список доступных перегрузок.

Replace Options

Алгоритм<regex_replace()>использует дополнительный параметр битмаски для управления форматированием. Возможные значения битмаски:

Table 43.7. Format Flags

Флаг

значение

<format_default>

Recognize the ECMA-262 format sequences (see below).

format_first_only

Only replace the first match, not all of them.

format_no_copy

Не копируйте части входной последовательности, которые не соответствовали регексу выходной последовательности.

format_literal

Treat the format string as a literal; that is, don't recognize any escape sequences.

<format_perl>

Recognize the Perl format sequences (see below).

<format_sed>

Recognize the sed format sequences (see below).

format_all

В дополнение к последовательностям формата Perl, распознайте некоторые последовательности формата Boost.


Эти флаги живут в пространстве имен<xpressive::regex_constants>. Если параметр замещения является функциональным объектом вместо строки, флаги<format_literal>,<format_perl>,<format_sed>и<format_all>игнорируются.

The ECMA-262 Format Sequences

Если вы не указали диалект строк замены с одним из флагов формата выше, вы получите диалект, определенный ECMA-262, стандартом для ECMAScript. В таблице ниже показаны последовательности выхода, распознаваемые в режиме ECMA-262.

Table 43.8. Format Escape Sequences

Последовательность побега

значение

$1, $2, etc.

соответствующий субматч

$&

the full match

$`

Префикс матча

$'

the match suffix

<$$>

a literal '$' character


Любая другая последовательность, начинающаяся с<'$'>, просто представляет себя. Например, если строка формата<"$a">, то<"$a">будет вставлена в выходную последовательность.

The Sed Format Sequences

При указании флага<format_sed>на<regex_replace()>распознаются следующие последовательности побега:

Table 43.9. Sed Format Escape Sequences

Последовательность побега

значение

\1, \2, etc.

The corresponding sub-match

<&>

the full match

\a

A literal '\a'

<\e>

Буквально<char_type(27)>

<\f>

Буквально<'\f'>

\n

Буквально<'\n'>

\r

Буквально<'\r'>

<\t>

A literal '\t'

<\v>

A literal '\v'

<\xFF>

A literal char_type(0xFF), where F is any hex digit

\x{FFFF}

A literal char_type(0xFFFF), where F is any hex digit

<\cX>

The control character X


The Perl Format Sequences

При указании флага<format_perl>на<regex_replace()>распознаются следующие последовательности выхода:

Table 43.10. Perl Format Escape Sequences

Последовательность побега

значение

$1, $2, etc.

соответствующий субматч

$&

the full match

$`

Префикс матча

$'

the match suffix

<$$>

a literal '$' character

\a

A literal '\a'

<\e>

Буквально<char_type(27)>

<\f>

Буквально<'\f'>

\n

Буквально<'\n'>

\r

Буквально<'\r'>

<\t>

A literal '\t'

<\v>

A literal '\v'

<\xFF>

A literal char_type(0xFF), where F is any hex digit

\x{FFFF}

A literal char_type(0xFFFF), where F is any hex digit

<\cX>

The control character X

<\l>

Make the next character lowercase

<\L>

Сделайте остальную часть нижнего регистра замены до следующего<\E>

\u

Make the next character uppercase

\U

Make the rest of the substitution uppercase until the next \E

<\E>

Прекратить<\L>или<\U>

\1, \2, etc.

The corresponding sub-match

<\g<name>>

Имяимя


The Boost-Specific Format Sequences

При указании флага<format_all>на<regex_replace()>распознанные последовательности побега те же, что и выше для<format_perl>. Кроме того, признаются условные выражения следующей формы:

?Ntrue-expression:false-expression

гдеN— десятичная цифра, представляющая подматчевую. Если в полном матче участвовал соответствующий субматч, то заменаистинное выражение. В противном случае этоложное выражение. В этом режиме можно использовать парены<()>для группировки. Если вы хотите буквальный paren, вы должны избежать его как<\(>.

Formatter Objects

Строки формата не всегда достаточно выразительны для всех ваших потребностей в замене текста. Рассмотрим простой пример желания сопоставить входные строки с выходными строками, как вы можете сделать с переменными среды. Вместо форматастрокидля этого вы использовали бы формататоробъекта. Рассмотрим следующий код, который находит встроенные переменные среды формы<"$(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

Формат вызова

Тип возврата

Семантика

<fmt(what)>

Range of characters (e.g. std::string) or null-terminated string

Струна, соответствующая регексу, заменяется строкой, возвращаемой форматером.

fmt(what, out)

OutputIterator

The formatter writes the replacement string into out and returns out.

fmt(what, out, flags)

OutputIterator

The formatter writes the replacement string into out and returns out. The flags parameter is the value of the match flags passed to the regex_replace() algorithm.


Formatter Expressions

Помимо форматастроки формататораобъектов,<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<>>для сокращения входных последовательностей.

Overview

Вы инициализируете<regex_token_iterator<>>с входной последовательностью, регексом и некоторыми дополнительными параметрами конфигурации.<regex_token_iterator<>>будет использовать<regex_search()>, чтобы найти первое место в последовательности, которая соответствует регексу. При упоминании<regex_token_iterator<>>возвращаеттокенв форме<std::basic_string<>>. Какую строку он возвращает, зависит от параметров конфигурации. По умолчанию она возвращает строку, соответствующую полному совпадению, но она также может возвращать строку, соответствующую определенному отмеченному субвыражению, или даже часть последовательности, котораяне соответствовала. Когда вы увеличите<regex_token_iterator<>>, он перейдет к следующему токену. Какой токен следующий, зависит от параметров конфигурации. Это может быть просто другое обозначенное суб-выражение в текущем матче, или это может быть часть или весь следующий матч. Или это может быть та часть, котораяне соответствовала.

Как видите,<regex_token_iterator<>>может многое сделать. Это затрудняет описание, но некоторые примеры должны прояснить это.

Example 1: Simple Tokenization

В этом примере<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

Example 2: Simple Tokenization, Reloaded

Этот пример также использует<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

Example 3: Simple Tokenization, Revolutions

Этот пример также использует<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

Example 4: Not-So-Simple Tokenization

Этот пример похож на предыдущий, за исключением того, что вместо токенизации только лет эта программа превращает дни, месяцы и годы в токены. Когда мы передаем массив целых чисел<{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>предписывает<regex_token_iterator<>>сначала принять значение 2-го подматча, затем 1-го подматча и, наконец, 3-го. Пополнение итератора снова дает ему указание использовать<regex_search()>снова, чтобы найти следующий матч. В этот момент процесс повторяется — итератор токенов принимает значение 2-го подматча, затем 1-го и так далее.

Overview

Для сложных регулярных выражений, работа с пронумерованными захватами может быть болью. Подсчет левых скобок, чтобы выяснить, какой захват ссылаться, не весело. Менее забавным является тот факт, что простое редактирование обычного выражения может привести к тому, что захвату будет присвоен новый номер, недействительный код, который ссылается на него на старый номер.

Другие двигатели регулярного выражения решают эту проблему с помощью функции, называемой, называемой захватами. Эта функция позволяет назначить имя для захвата и ссылаться на захват по имени, а не по номеру. Xpressive также поддерживает именованные захваты, как в динамических, так и в статических регексах.

Dynamic Named Captures

Для динамических регулярных выражений 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

Static Named Captures

Если вы используете статические регулярные выражения, создание и использование именованных захватов еще проще. Вы можете использовать тип<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] Note

Вы должны включить<<boost/xpressive/regex_actions.hpp>>, чтобы использовать выражения формата.

Overview

Одним из ключевых преимуществ представления регексов как выражений C++ является возможность легко ссылаться на другой код C++ и данные из регекса. Это позволяет программировать идиомы, которые невозможны с другими библиотеками регулярных выражений. Особо следует отметить способность одного регекса ссылаться на другой, что позволяет создавать грамматику из регулярных выражений. В этом разделе описывается, как встроить один регекс в другой по значению и по ссылке, как объекты регекса ведут себя, когда они относятся к другим регексам, и как получить доступ к дереву результатов после успешного анализа.

Embedding a Regex by Value

<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] Note

Обратите внимание, что<re= bow>> re>> eow>действительно, а неопределяют рекурсивное регулярное выражение, поскольку объекты регекса встраиваются по умолчанию. Следующий раздел показывает, как определить рекурсивное регулярное выражение путем встраивания регекса посредством ссылки.

Embedding a Regex by Reference

Если вы хотите иметь возможность создавать рекурсивные регулярные выражения и контекстно-свободные грамматики, встраивания регекса по значению недостаточно. Вы должны быть в состоянии сделать свои регулярные выражения самореферентными. Большинство двигателей с регулярным выражением не дают вам такой мощности, но впечатляет.

[Tip] 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>создает цикл, который будет выполняться рекурсивно.

Building a Grammar

Как только мы позволяем себе ссылаться в наших регулярных выражениях, джинн выходит из бутылки, и возможны всевозможные забавные вещи. В частности, теперь мы можем строить грамматику из регулярных выражений. Давайте посмотрим на пример грамматики учебника: скромный калькулятор.

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] Tip

Встроен по стоимости, если это возможно

В общем, предпочитают встраивать регулярные выражения по значению, а не по ссылке. Это включает в себя одно меньшее опосредование, что делает ваши шаблоны немного быстрее. Кроме того, ценностная семантика проще и облегчит ваши грамматики. Не беспокойтесь о расходах на «копирование» регекса. Каждый объект regex делится своей реализацией со всеми своими копиями.

Dynamic Regex Grammars

Используя<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;
}

Как и в случае с грамматикой статического регекса, вложенные вызовы регекса создают вложенные результаты соответствия (см.).Вложенные результатыниже. Результатом является полное дерево разбора для струны, которая соответствует. В отличие от статических регексов, динамические регексы всегда встроены посредством ссылки, а не значения.

Cyclic Patterns, Copying and Memory Management, Oh My!

Примеры калькулятора выше поднимают ряд очень сложных проблем управления памятью. Каждый из четырех объектов регекса относится друг к другу, некоторые прямо, а некоторые косвенно, некоторые по значению, а некоторые по ссылке. Что, если мы вернем одного из них из функции и позволим другим выйти из-под контроля? Что станет со ссылками? Ответ заключается в том, что объекты регекса считаются внутренними ссылками, так что они сохраняют свои объекты регекса живыми до тех пор, пока они в них нуждаются. Таким образом, передача объекта регекса по значению никогда не является проблемой, даже если она относится к другим объектам регекса, которые вышли из сферы действия.

Те из вас, кто занимался подсчетом ссылок, вероятно, знакомы с его ахиллесовой пятой: циклическими ссылками. Если объекты регекса подсчитываются, что происходит с циклами, подобными тем, которые созданы в примерах калькулятора? Они просочились? Ответ - нет, они не просочились. Объект<basic_regex<>>имеет сложный код отслеживания ссылок, который гарантирует, что даже циклические грамматики регекса очищаются, когда последняя внешняя ссылка исчезает. Так что не волнуйся. Создавайте циклические грамматики, передайте объекты регекса и копируйте их все, что хотите. Это быстро и эффективно и гарантированно не утечка или не приводит к свисанию ссылок.

Nested Regexes and Sub-Match Scoping

Вложенные регулярные выражения поднимают вопрос о подматчевом определении. Если и внутренний, и внешний регекс пишут и читают из одного и того же вектора подсоответствия, то наступает хаос. Внутренний регекс будет топтать на подматчи, написанные внешним регексом. Например, что это делает?

sregex inner = sregex::compile( "(.)\\1" );
sregex outer = (s1= _) >> inner >> s1;

Автор, вероятно, не намеревался, чтобы внутренний регекс перезаписывал подматч, написанный внешним регексом. Проблема особенно остро стоит, когда внутренний регекс принимается от пользователя в качестве ввода. Автор не имеет возможности узнать, будет ли внутренний регекс топтать вектор подматчевого состояния или нет. Это явно неприемлемо.

Вместо этого на самом деле происходит то, что каждый вызов вложенного регекса получает свой собственный охват. Подматчи относятся к этой сфере. То есть, каждый вложенный вызов регекса получает свою собственную копию вектора подматча, чтобы играть, поэтому нет возможности для внутреннего регекса топать на подматчи внешнего регекса. Так, например, регекс<outer>, определенный выше, будет соответствовать<"ABBA">.

Nested Results

Если вложенные регексы имеют свои собственные подматчи, должен быть способ доступа к ним после успешного матча. На самом деле, есть. После<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] Tip

См. определениеoutput_nested_resultsв разделеПримеры.

Filtering 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

Overview

Представьте, что вы хотите разобрать входную строку и построить из нее<std::map<>>. Для чего-то подобного соответствия обычному выражению недостаточно. Вы хотитесделать что-то, когда части вашего обычного выражения совпадают. Xpressive позволяет прикреплять семантические действия к частям статических регулярных выражений. Этот раздел показывает, как.

Semantic Actions

Рассмотрим следующий код, который использует семантические действия 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] Note

Чтобы использовать семантические действия со своими статическими регексами, вы должны<#include<boost/xpressive/regex_actions.hpp>>

Как это работает? Как и остальная часть статического регулярного выражения, часть между скобками является шаблоном выражения. Он кодирует действие и выполняет его позже. Выражение<ref(result)>создает ленивую отсылку к объекту<result>. Большее выражение<ref(result)[s1]>— ленивая операция индекса карты. Позже, когда это действие выполняется,<s1>заменяется первым<sub_match<>>. Когда<as<int>(s2)>исполняется,<s2>заменяется вторым<sub_match<>>. Действие<as<>>преобразует свой аргумент в запрашиваемый с помощью Boost. Lexical_cast. Результатом всего действия является вставка нового слова/целой пары в карту.

[Note] Note

Существует важное различие между функцией<boost::ref()>в<<boost/ref.hpp>>и<boost::xpressive::ref()>в<<boost/xpressive/regex_actions.hpp>>. Первая возвращает равнину<reference_wrapper<>>, которая во многих отношениях ведет себя как обычная ссылка. Напротив,<boost::xpressive::ref()>возвращаетленивуюссылку, которую вы можете использовать в выражениях, которые выполняются лениво. Вот почему мы можем сказать<ref(result)[s1]>, хотя<result>не имеет<operator[]>, который принял бы<s1>.

В дополнение к подматчевым заполнителям<s1>,<s2>и т.д., вы также можете использовать заполнитель<_>в действии, чтобы ссылаться на строку, соответствующую подвыражению, к которому прикреплено действие. Например, вы можете использовать следующий регекс, чтобы сопоставить кучу цифр, интерпретировать их как целое число и назначить результат локальной переменной:

int i = 0;
// Here, _ refers back to all the
// characters matched by (+_d)
sregex rex = (+_d)[ ref(i) = as<int>(_) ];

Lazy Action Execution

Что именно означает прикрепить действие к части регулярного выражения и выполнить совпадение? Когда выполняется действие? Если действие является частью повторного подвыражения, выполняется ли оно один или несколько раз? И если подвыражение изначально совпадает, но в конечном итоге терпит неудачу, потому что остальная часть обычного выражения не соответствует, выполняется ли действие вообще?

Ответ заключается в том, что по умолчанию действия выполняютсялениво. Когда подэкспрессия соответствует строке, ее действие помещается в очередь вместе с текущими значениями любых подматчей, к которым относится действие. Если алгоритм матча должен отступить, действия выскакивают из очереди по мере необходимости. Только после того, как весь регекс успешно совпал, действия фактически выполняются. Все они исполняются сразу, в том порядке, в котором они были добавлены в очередь, как последний шаг перед возвращением<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)>стоит в очереди три раза: один раз за каждую найденную цифру. Но тольковыполненодважды: один раз для каждой цифры, которая предшествует<'!'>символу. Когда персонаж<'?'>встречается, алгоритм совпадения отступает, удаляя окончательное действие из очереди.

Immediate Action Execution

Когда вы хотите, чтобы семантические действия выполнялись немедленно, вы можете обернуть подвыражение, содержащее действие в<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] Note

Подобно<keep()>, действия в<before()>и<after()>также выполняются рано, когда их суб-выражения совпадают.

Lazy Functions

До сих пор мы видели, как писать семантические действия, состоящие из переменных и операторов. Но что, если вы хотите получить функцию от семантического действия? 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 = {{}};

Referring to Local Variables

Как мы видели в примерах выше, мы можем ссылаться на локальные переменные в действиях, используя<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<>>может.

Referring to Non-Local Variables

В начале этого раздела мы использовали регекс с семантическим действием, чтобы разобрать строку пар слова/целого числа и вставить их в<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] 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

User-Defined Assertions

Вы, вероятно, уже знакомы с утверждениями регулярного выражения. В 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] 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 дней.)

Overview

Таблицы символов могут быть встроены в регулярную экспрессию только с<std::map<>>. Ключи карты - это строки, которые необходимо сопоставить, а значения карты - это данные, которые должны быть возвращены в ваше семантическое действие. Хпрессивные атрибуты, названные<a1>,<a2>, до<a9>, удерживают значение, соответствующее соответствующему ключу, чтобы его можно было использовать в семантическом действии. Значение по умолчанию может быть определено для атрибута, если символ не найден.

Symbol Tables

Впечатляющая таблица символов - это просто<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()>.

Attributes

В обычном выражении можно использовать до девяти атрибутов. Они названы<a1>,<a2>, ...,<a9>в пространстве имен<boost::xpressive>. Тип атрибута такой же, как и второй компонент карты, который ему присваивается. Значение по умолчанию для атрибута может быть определено в семантическом действии с синтаксисом<(a1 |default-value>.

Атрибуты должным образом ограничены, поэтому вы можете делать сумасшедшие вещи, такие как:<((a1=sym1) >>(a1=sym2)[ref(x)=a1])[ref(y)=a1]>. Внутреннее семантическое действие видит внутреннее<a1>, а внешнее семантическое действие видит внешнее. Они могут даже иметь разные типы.

[Note] Note

Xpressive создает скрытую тройку поиска с карты, чтобы она могла быстро искать. Если BOOST_DISABLE_THREADS определена, то скрытая тройка поиска «саморегулируется», поэтому после каждого поиска она реструктурируется для повышения эффективности будущих поисков на основе частоты предыдущих поисков.

Overview

Соответствие обычного выражения строке часто требует информации, зависящей от местоположения. Например, как выполняются бесчувственные сравнения? Локально-чувствительное поведение фиксируется в классе признаков. xpressive предоставляет три шаблона классов черт:<cpp_regex_traits<>>,<c_regex_traits<>>и<null_regex_traits<>>. Первый обертывает a<std::locale>, второй обертывает глобальную область C, а третий - тип загнутых признаков для использования при поиске нехарактерных данных. Все шаблоны черт соответствуют. Концепция регексных черт.

Setting the Default Regex Trait

По умолчанию, xpressive использует<cpp_regex_traits<>>для всех моделей. Это заставляет все объекты регекса использовать глобальный<std::locale>. Если вы скомпилируете с<BOOST_XPRESSIVE_USE_C_TRAITS>определенным, то xpressive будет использовать<c_regex_traits<>>по умолчанию.

Using Custom Traits with Dynamic Regexes

Для создания динамического регекса, использующего пользовательский объект, необходимо использовать<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>действуют как фабрики-регексы. После того, как они были пропитаны локализацией, каждый созданный ими объект регекса будет использовать эту локализацию.

Using Custom Traits with Static Regexes

Если вы хотите, чтобы конкретный статический регекс использовал другой набор признаков, вы можете использовать специальный модификатор шаблона<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 );

Searching Non-Character Data With 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);

Сжимайте максимальную производительность из-за этих советов и трюков.

Compile Patterns Once And Reuse Them

Составление регекса (динамического или статического)далекодороже, чем выполнение матча или поиска. Если у вас есть возможность, предпочтите компилировать шаблон в<basic_regex<>>.возражать один раз и использовать его повторно, а не воссоздавать снова и снова.

Поскольку объекты<basic_regex<>>не мутируются ни одним из алгоритмов регекса, они полностью защищены от потоков после завершения их инициализации (и любой грамматики, членами которой они являются). Самый простой способ повторного использования ваших шаблонов — это просто сделать ваш<basic_regex<>>.Объекты «статического конста».

Reuse match_results<> Objects

Объект<match_results<>>кэширует динамически выделенную память. По этой причине гораздо лучше повторно использовать один и тот же объект<match_results<>>, если вам нужно выполнить много поисковых запросов.

Пещера:<match_results<>>объекты не являются нитями-безопасными, поэтому не используйте их повторно.

Prefer Algorithms That Take A match_results<> Object

Это является следствием предыдущего совета. Если вы выполняете несколько поисков, вы должны предпочесть алгоритмы регекса, которые принимают объект<match_results<>>, а не те, которые этого не делают, и вы должны повторно использовать тот же<match_results<>>.каждый раз. Если вы не предоставляете<match_results<>>объект, временный будет создан для вас и отброшен, когда алгоритм вернется. Любая память, кэшированная в объекте, будет размещена и должна быть перераспределена в следующий раз.

Prefer Algorithms That Accept Iterator Ranges Over Null-Terminated Strings

xpressive обеспечивает перегрузки алгоритмов<regex_match()>и<regex_search()>, которые работают на нулевых строках в стиле C. Вы должны предпочесть перегрузки, которые принимают диапазоны итераторов. Когда вы передаете нулевую строку алгоритму регекса, конечный итератор вычисляется немедленно по телефону<strlen>. Если вы уже знаете длину строки, вы можете избежать этих накладных расходов, позвонив в алгоритмы регекса с парой<[begin,end)>.

Use Static Regexes

В среднем статические регексы выполняются на 10-15% быстрее, чем их динамические аналоги. Стоит ознакомиться со статическим диалектом регекса.

Understand syntax_option_type::optimize

Флаг<optimize>сообщает компилятору regex, чтобы он потратил дополнительное время на анализ шаблона. Это может привести к тому, что некоторые шаблоны будут выполняться быстрее, но это увеличивает время на компиляцию шаблона и часто увеличивает количество памяти, потребляемой шаблоном. Если вы планируете повторно использовать свой шаблон,<optimize>обычно является выигрышем. Если вы используете шаблон только один раз, не используйте<optimize>.

Common Pitfalls

Имейте в виду следующие советы, чтобы избежать наступления в выбоинах с депрессией.

Create Grammars On A Single Thread

С помощью статических регексов вы можете создавать грамматики, вставляя регексы друг в друга. При составлении внешнего регекса изменяются как внешние, так и внутренние объекты регекса, а также все объекты регекса, к которым они относятся прямо или косвенно. По этой причине для глобальных объектов регекса опасно участвовать в грамматике. Лучше всего создавать грамматику регекса из одной нити. После создания результирующая грамматика regex может быть выполнена из нескольких потоков без проблем.

Beware Nested Quantifiers

Это ловушка, общая для многих двигателей регулярного выражения. Некоторые модели могут вызвать экспоненциально плохую производительность. Часто эти паттерны включают в себя один количественный термин, вложенный в другой квантор, такой как<"(a*)*">, хотя во многих случаях проблему труднее обнаружить. Остерегайтесь шаблонов, которые имеют вложенные количественные показатели.

CharT requirements

Если тип<BidiIterT>используется в качестве шаблонного аргумента<basic_regex<>>, то<CharT>является<iterator_traits<BidiIterT>::value_type>. Тип<CharT>должен иметь тривиальный конструктор по умолчанию, конструктор копий, оператор назначения и деструктор. Кроме того, для объектов должны быть выполнены следующие требования:<c>типа<CharT>,<c1>и<c2>типа<CharTconst>и<i>типа<int>:

Table 43.14. CharT Requirements

Выражение

Тип возврата

Утверждение/Примечание/Предварительное/Пост-условие

CharT c

CharT

Default constructor (must be trivial).

<CharTc(c1)>

CharT

Copy constructor (must be trivial).

<c1= c2>

CharT

Назначение оператора (должно быть тривиальным).

<c1== c2>

<bool>

true if c1 has the same value as c2.

<c1!= c2>

<bool>

true if c1 and c2 are not equal.

<c1< c2>

<bool>

true if the value of c1 is less than c2.

c1 > c2

<bool>

<true>, если значение<c1>больше<c2>

.

<c1<= c2>

<bool>

<true>, если<c1>меньше или равно<c2>

.

<c1>= c2>

<bool>

<true>, если<c1>больше или равно<c2>

.

intmax_t i = c1

<int>

CharT must be convertible to an integral type.

CharT c(i);

CharT

<CharT>должны быть построены из интегрального типа.


Traits Requirements

В следующей таблице<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

Выражение

Тип возврата

Утверждение/Примечание
Пред. Состояние

X::char_type

CharT

The character container type used in the implementation of class template basic_regex<>.

X::string_type

<std::basic_string<CharT>>или<std::vector<CharT>>

<X::locale_type>

Implementation defined

A copy constructible type that represents the locale used by the traits class.

X::char_class_type

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.

<X::hash(c)>

<unsignedchar>

Приносит значение между<0>и<UCHAR_MAX>включительно.

v.widen(ch)

CharT

Widens the specified char and returns the resulting CharT.

<v.in_range(r1, r2, c)>

<bool>

For any characters r1 and r2, returns true if r1 <= c && c <= r2. Requires that r1 <= r2.

v.in_range_nocase(r1, r2, c)

<bool>

Для персонажей<r1>и<r2>возвращается<true>, если есть какой-то персонаж<d>, для которого<v.translate_nocase(d) ==v.translate_nocase(c)>и<r1 <=d &&d <=r2>. Это необходимо<r1<=r2>

.

v.translate(c)

X::char_type

Возвращает символ так, что для любого персонажа<d>, который должен считаться эквивалентным<c>, затем<v.translate(c) ==v.translate(d)>

.

<v.translate_nocase(c)>

X::char_type

Для всех знаков<C>, которые должны считаться эквивалентными<c>, когда сравнения должны выполняться без учета случая, тогда<v.translate_nocase(c) ==v.translate_nocase(C)>

.

v.transform(F1, F2)

X::string_type

Returns a sort key for the character sequence designated by the iterator range [F1, F2) such that if the character sequence [G1, G2) sorts before the character sequence [H1, H2) then v.transform(G1, G2) < v.transform(H1, H2).

<v.transform_primary(F1, F2)>

X::string_type

Returns a sort key for the character sequence designated by the iterator range [F1, F2) such that if the character sequence [G1, G2) sorts before the character sequence [H1, H2) when character case is not considered then v.transform_primary(G1, G2) < v.transform_primary(H1, H2).

v.lookup_classname(F1, F2)

X::char_class_type

Преобразует последовательность символов, обозначенную диапазоном итераторов<[F1,F2)>, в тип битмаски, который впоследствии может быть передан<isctype>. Значения, возвращенные из<lookup_classname>, могут быть безопасными по битам или вместе. Возвращается<0>, если последовательность символов не является именем класса символов, распознаваемого<X>. Возвращенная стоимость должна быть независимой от символов в последовательности.

v.lookup_collatename(F1, F2)

X::string_type

Возвращает последовательность символов, которая представляет коллатинговый элемент, состоящий из последовательности символов, обозначенной диапазоном итераторов<[F1,F2)>. Возвращает пустую строку, если последовательность символов не является действительным коллаирующим элементом.

v.isctype(c, v.lookup_classname(F1, F2))

<bool>

Returns true if character c is a member of the character class designated by the iterator range [F1, F2), false otherwise.

v.value(c, i)

<int>

Returns the value represented by the digit c in base i if the character c is a valid digit in base i; otherwise returns -1.
[Note: the value of i will only be 8, 10, or 16. -end note]

u.imbue(loc)

<X::locale_type>

Имбуи<u>с локализацией<loc>, возвращает предыдущую локализацию, используемую<u>.

v.getloc()

<X::locale_type>

Возвращает текущее местоположение, используемое<v>

.

Acknowledgements

Этот раздел адаптирован с эквивалентной страницы в документацииBoost.Regexи с предложениядля добавления регулярных выражений в Стандартную библиотеку.

Ниже вы можете найти шесть полных образцов.

See if a whole string matches a 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


Верхний

See if a string contains a sub-string that matches a regex

Обратите внимание в этом примере, как мы используем пользовательские<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
/


Верхний

Replace all sub-strings that match a regex

Следующая программа находит даты в строке и отмечает их псевдо-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.


Верхний

Find all the sub-strings that match a regex and step through them one at a time

Следующая программа находит слова в строке с широкими символами. Используется<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


Верхний

Split a string into tokens that each match a regex

Следующая программа находит время гонки в строке и отображает сначала минуты, а затем секунды. Используется<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


Верхний

Split a string using a regex as a delimiter

Следующая программа берет некоторый текст, который был отмечен 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.}


Верхний

Display a tree of nested results

Вот класс помощников, чтобы продемонстрировать, как вы можете отображать дерево вложенных результатов:

// 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 ) );
    }
};

Верхний



Большое спасибо Дэвиду Дженкинсу, который привел этот пример.


PrevUpHomeNext

Статья User's Guide раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 43. Boost.Xpressive может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 43. Boost.Xpressive ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 17:20:23/0.046698093414307/0