Статья знакомит с использованием регулярных выражений в C++ с помощью TR1 (C++ Standards Committee Technical Report 1). Она рассчитана на людей знакомых с регулярными выражениями, но не умеют использовать их в C++.
C++ TR1 поддерживает синтаксис регулярных выражений нескольких типов, достаточно указать соответствующий необязательный флаг в конструкторе класса. Реализация от Microsoft поддерживает 6 следующих типов:
В реализации от Microsoft по умолчанию используется ECMAScript, идентичный регулярным выражениям языка ECMAScript (JavaScipt), которые во многом совпадают с таковыми в Perl 5.
Количество реализаций никак не регулируется. Например, Boost добавил тип perl, который поддерживает синтаксис Perl 5 намного лучше, чем тип ECMAScript.
Функции для работы с регулярными выражениями в C++ объявлены в заголовочном файле и содержаться в пространстве имен std::tr1.
Первым сюрпризом при работе с регулярными выражениями в C++ заключается в том, что функция regex_match не ищет совпадение в обычном смысле. Она возвращает true только в случае, когда строка совпадает регулярным выражением. Функция regex_search больше похожа на работу оператора поиска совпадений, такого как m// в Perl.
На примере следующего кода, можно увидеть, как работают функции regex_search и regex_match.
Функция regex_match вернет false, так как строка str отличается от регулярного выражения rx. А функция regex_search вернут true, так как регулярное выражение содержится в строке.
В Perl после поиска найденные совпадения хранятся в переменных $1, $2 и т.д. Аналогичным образом, в C++ результат поиска хранятся в объекте match_result. Однако, Perl всегда создает $n-переменные, а C++ никуда не сохраняет результаты до тех пор, пока вы не вызовете перегруженную функцию regex_search, которая принимает объект match_result. Класс match_result – шаблонный, часто используется класс cmatch, который объявляют следующим образом:
Следующий пример иллюстрирует, как получить найденные совпадения.
Результатом работы этого кода будет
Заметьте, что res[n] соответствует $n-переменным в Perl.
Следующий код заменит слово «world» на «planet» в строке «Hello world». Результат будет сохранен в строке str2, а исходная строка str останется прежней.
Заметьте, что функция regex_replace не меняет значение своих аргументов, как это делает Perl-команда s/world/planet/.
Заметьте также, что третий параметр regex_replace должен быть объект класса string а не строковым литералом. Можно, однако, заменить временную переменную «replacement» на строковый литерал приведенный к классу string:
По умолчанию, все соответствия, удовлетворяющие шаблону, будут заменены. Если в предыдущем примере заменить строку str на «Hello world world», то результатом функции regex_replace будет строка «Hello planet planet». Для того чтобы заменить только первое совпадение, необходимо добавить флаг
в качестве 4-ого параметра функции regex_replace.
Поскольку поведение по умолчанию функции regex_replace представляет собой глобальную замену, то она является аналогом оператора s///g в Perl. С флагом format_first_only она становится аналогом оператора s///.
Регулярные выражения в C++ не так удобно, как в таких языках, как Perl, которые имеют встроенную поддержку регулярных выражений. Одна из причин – это escape-последовательности. Для добавления знака обратного слэша «\», необходимо в исходном коде писать «\\».
Регулярные выражения в C++ по умолчанию чувствительны к регистру, как в Perl и многих других средах. Чтобы указать, что регулярное выражение не чувствительно к регистру, добавьте флаг std::tr1::regex_constants::icase как второй аргумент в конструкторе regex. Флаги, переданные в конструктор, могут быть объединены. Так что если вы указываете флаг для типа регулярных выражений, вы можете с помощью оператора побитого «ИЛИ» добавить флаг нечувствительности к регистру.
C++ TR1 поддерживает синтаксис регулярных выражений нескольких типов, достаточно указать соответствующий необязательный флаг в конструкторе класса. Реализация от Microsoft поддерживает 6 следующих типов:
- basic
- extended
- ECMAScript
- awk
- grep
- egrep
В реализации от Microsoft по умолчанию используется ECMAScript, идентичный регулярным выражениям языка ECMAScript (JavaScipt), которые во многом совпадают с таковыми в Perl 5.
Количество реализаций никак не регулируется. Например, Boost добавил тип perl, который поддерживает синтаксис Perl 5 намного лучше, чем тип ECMAScript.
Заголовочный файл и пространство имен
Функции для работы с регулярными выражениями в C++ объявлены в заголовочном файле и содержаться в пространстве имен std::tr1.
Поиск совпадений
Первым сюрпризом при работе с регулярными выражениями в C++ заключается в том, что функция regex_match не ищет совпадение в обычном смысле. Она возвращает true только в случае, когда строка совпадает регулярным выражением. Функция regex_search больше похожа на работу оператора поиска совпадений, такого как m// в Perl.
На примере следующего кода, можно увидеть, как работают функции regex_search и regex_match.
std::string str = "Hello world";
std::tr1::regex rx("ello");
regex_match(str.begin(), str.end(), rx)
regex_search(str.begin(), str.end(), rx)
Функция regex_match вернет false, так как строка str отличается от регулярного выражения rx. А функция regex_search вернут true, так как регулярное выражение содержится в строке.
Получение совпадений
В Perl после поиска найденные совпадения хранятся в переменных $1, $2 и т.д. Аналогичным образом, в C++ результат поиска хранятся в объекте match_result. Однако, Perl всегда создает $n-переменные, а C++ никуда не сохраняет результаты до тех пор, пока вы не вызовете перегруженную функцию regex_search, которая принимает объект match_result. Класс match_result – шаблонный, часто используется класс cmatch, который объявляют следующим образом:
typedef match_result<const char *> cmatch;
Следующий пример иллюстрирует, как получить найденные совпадения.
std::tr1::cmatch res;
str = "Egg prices
";
std::tr1::regex rx("<h(.)>([^<]+)");
std::tr1::regex_search(str.c_str(), res, rx);
std::cout << res[1] << ". " << res[2] << "\n";
Результатом работы этого кода будет
2. Egg prices
Заметьте, что res[n] соответствует $n-переменным в Perl.
Замена найденных совпадений
Следующий код заменит слово «world» на «planet» в строке «Hello world». Результат будет сохранен в строке str2, а исходная строка str останется прежней.
std::string str = "Hello world";
std::tr1::regex rx("world");
std::string replacement = "planet";
std::string str2 = std::tr1::regex_replace(str, rx, replacement);
Заметьте, что функция regex_replace не меняет значение своих аргументов, как это делает Perl-команда s/world/planet/.
Заметьте также, что третий параметр regex_replace должен быть объект класса string а не строковым литералом. Можно, однако, заменить временную переменную «replacement» на строковый литерал приведенный к классу string:
regex_replace(str, rx, std::string("planet"))
По умолчанию, все соответствия, удовлетворяющие шаблону, будут заменены. Если в предыдущем примере заменить строку str на «Hello world world», то результатом функции regex_replace будет строка «Hello planet planet». Для того чтобы заменить только первое совпадение, необходимо добавить флаг
std::tr1::regex_constants::format_first_only
в качестве 4-ого параметра функции regex_replace.
Поскольку поведение по умолчанию функции regex_replace представляет собой глобальную замену, то она является аналогом оператора s///g в Perl. С флагом format_first_only она становится аналогом оператора s///.
Escape-последовательности
Регулярные выражения в C++ не так удобно, как в таких языках, как Perl, которые имеют встроенную поддержку регулярных выражений. Одна из причин – это escape-последовательности. Для добавления знака обратного слэша «\», необходимо в исходном коде писать «\\».
Чувствительность к регистру
Регулярные выражения в C++ по умолчанию чувствительны к регистру, как в Perl и многих других средах. Чтобы указать, что регулярное выражение не чувствительно к регистру, добавьте флаг std::tr1::regex_constants::icase как второй аргумент в конструкторе regex. Флаги, переданные в конструктор, могут быть объединены. Так что если вы указываете флаг для типа регулярных выражений, вы можете с помощью оператора побитого «ИЛИ» добавить флаг нечувствительности к регистру.