Permalink
Cannot retrieve contributors at this time
description | title | ms.date | f1_keywords | helpviewer_keywords | ms.assetid | ||
---|---|---|---|---|---|---|---|
Learn more about: regex_error Class |
regex_error Class |
09/10/2018 |
|
regex_error class |
3333a1a3-ca6f-4612-84b2-1b4c7e3db5a4 |
regex_error Class
Reports a bad basic_regex object.
Syntax
class regex_error : public std::runtime_error
Remarks
The class describes an exception object thrown to report an error in the construction or use of a basic_regex
object.
Constructors
Constructor | Description |
---|---|
regex_error | Constructs the object. |
Member functions
Member function | Description |
---|---|
code | Returns the error code. |
Requirements
Header: <regex>
Namespace: std
Example
// std__regex__regex_error.cpp // compile with: /EHsc #include <regex> #include <iostream> int main() { std::regex_error paren(std::regex_constants::error_paren); try { std::regex rx("(a"); } catch (const std::regex_error& rerr) { std::cout << "regex error: " << (rerr.code() == paren.code() ? "unbalanced parentheses" : "") << std::endl; } catch (...) { std::cout << "unknown exception" << std::endl; } return (0); }
regex error: unbalanced parentheses
regex_error::code
Returns the error code.
regex_constants::error_code code() const;
Remarks
The member function returns the value that was passed to the object’s constructor.
regex_error::regex_error
Constructs the object.
regex_error(regex_constants::error_code error);
Parameters
error
The error code.
Remarks
The constructor constructs an object that holds the value error.
See also
<regex>
regex_constants Class
<regex> functions
regex_iterator Class
<regex> operators
regex_token_iterator Class
regex_traits Class
<regex> typedefs
Определено в заголовке <regex> |
||
---|---|---|
class regex_error; |
(since C++11) |
Свойство определяет тип объекта исключения,брошенного для сообщения об ошибках в библиотеке регулярных выражений.
Inheritance diagram.
Member functions
(constructor) |
строит regex_error объект(функция публичного члена) |
operator= |
заменяет объект regex_error (функция публичного члена) |
code |
получает std::regex_constants::error_type для regex_error (функция публичного члена) |
Унаследовано от std::runtime_error
Наследуется от std :: exception
Member functions
(destructor) [virtual] |
уничтожает объект исключения (виртуальная публичная функция-член std::exception ) |
what [virtual] |
возвращает пояснительную строку (виртуальная публичная функция-член std::exception ) |
Example
#include <regex> #include <iostream> int main() { try { std::regex re("[a-b][a"); } catch (const std::regex_error& e) { std::cout << "regex_error caught: " << e.what() << 'n'; if (e.code() == std::regex_constants::error_brack) { std::cout << "The code was error_brackn"; } } }
Possible output:
regex_error caught: The expression contained mismatched [ and ]. The code was error_brack
C++
-
std::swap(std::match_results)
Специализирует алгоритм std::swap для std::match_results.
-
std::match_results<BidirIt,Alloc>::~match_results
Уничтожает объект match_results и связанные с ним вложенные совпадения.
-
std::regex_error::code
Возвращает std::regex_constants::error_type,который был передан конструктору std::regex_error.
-
std::regex_error::operator=
Присваивает содержимое с содержимым других.
-
1
- …
-
3464
-
3465
-
3466
-
3467
-
3468
- …
-
4464
-
Next
Время прочтения
10 мин
Просмотры 55K
Привет, Хабр. Будущих студентов курса «C++ Developer. Professional» приглашаем записаться на открытый урок по теме «Backend на современном C++»
А пока делимся традиционным переводом полезного материала.
Регулярные выражения (Regular expressions или, вкратце, regex — регулярки) — это пока что непопулярная и недооцененная тема в современном C++. Но в то же время разумное использование регулярных выражений может избавить вас от написания множества строчек кода. Если у вас уже есть какой-никакой опыт работы в индустрии, но вы не умеете использовать регулярные выражения — вы разбазариваете 20-30% своей продуктивности. Я настоятельно рекомендую вам освоить регулярные выражение, так как это единовременная инвестиция в себя (по известному принципу “learn once, write anywhere”).
/!: Изначально эта статья была опубликована в моем личном блоге. Если вам станет интересно и дальше читать мои самые актуальные статьи, вы можете подписаться на мою рассылку.
Изначально я хотел включить в эту статью сведения о регулярных выражениях вообще в целом. Но это не имеет особого смысла, так как уже существует множество людей и учебников, которые намного лучше меня вводят в регулярные выражения. Но все же я оставил небольшую часть, посвященную мотивации и изучению регулярок. В оставшейся части статьи я сосредоточусь на функциональности для работы с регулярным выражением, предоставляемой конкретно C++. И если вы уже имеете представление о регулярных выражениях, вы можете использовать приведенную ниже ассоциативную карту в качестве напоминания.
Примечание: стандартная библиотека C++ предлагает несколько различных разновидностей («flavours») синтаксиса регулярных выражений, но вариант по умолчанию (тот, который вы всегда должны использовать, и который я демонстрирую здесь) был полностью позаимствован из стандарта ECMAScript.
Мотивация
Я понимаю, инструментарий регулярок скуден и достаточно запутан. Рассмотрим приведенный ниже шаблон регулярного выражения, который извлекает время в 24-часовом формате (т.е. ЧЧ:ММ), в качестве примера.
b([01]?[0-9]|2[0-3]):([0-5]d)b
Вот да! Кто захочет возиться с этим непонятным текстом?
И все, что приходит вам в голову, глядя на это, на 100% небезосновательно. Я сам дважды откладывал изучение регулярных выражений по той же причине. Но, поверьте, этот неприглядный инструмент не так уж и плох.
Подход (↓), который я здесь описываю, не отнимет у вас больше 2-3 часов на изучение регулярных выражений, которые, по правде говоря, интуитивно понятны. После того, как вы их освоите, вы увидите, что с течением времени ваша инвестиция дает стабильные дивиденды.
Изучение регулярных выражений
Не нужно много гуглить и пытаться анализировать, какой учебник лучше. Вообще не тратьте время на такой анализ. Потому что в этом нет смысла. На данный момент (ну, если вы еще не знаете регулярные выражения) больше смысла имеет не пытаться угадать, где же лучше начать, а собственно начать изучение.
Просто не задумываясь перейдите на https://regexone.com. И пройдите все уроки. Поверьте мне, я перелопатил множество статей, курсов (<= этот бесплатный, кстати) и книг. Но это лучший вариант чтобы начать и не потерять мотивацию.
После этого, если у вас осталось желание порешать побольше задач и упражнений, рассмотрите приведенные ниже ссылки:
-
Упражнения на regextutorials.com
-
Практические задачи с регулярными выражениями на hackerrank
Пример std::regex и std::regexerror
int main() {
try {
static const auto r = std::regex(R"()"); // Escape sequence error
} catch (const std::regex_error &e) {
assert(strcmp(e.what(), "Unexpected end of regex when escaping.") == 0);
assert(e.code() == std::regex_constants::error_escape);
}
return EXIT_SUCCESS;
}
Вот видите! Я использую сырые строковые литералы. Вы также можете использовать обычную строку, но в таком случае вы должны использовать двойной бэкслеш () для escape-последовательности.
Текущая реализация std::regex
медленная (так как требует интерпретации регулярных выражений и создания структуры данных во время выполнения), раздувается и неизбежно требует динамического выделения памяти (не allocator aware). Будьте осторожны, если вы используете std::regex
в цикле (см. C++ Weekly — Ep 74 — std::regex optimize by Jason Turner). Кроме того, в ней есть только одна функция-член, которая, как я думаю, действительно может быть полезной — это std::regex::markcount()
, которая возвращает несколько групп захвата.
Более того, если вы используете несколько строк для создания шаблона регулярного выражения во время выполнения, то вам может потребоваться обработка исключений (например, std::regexerror)
, чтобы проверить его правильность.
Пример std::regex_search
int main() {
const string input = "ABC:1-> PQR:2;;; XYZ:3<<<"s;
const regex r(R"((w+):(w+);)");
smatch m;
if (regex_search(input, m, r)) {
assert(m.size() == 3);
assert(m[0].str() == "PQR:2;"); // Entire match
assert(m[1].str() == "PQR"); // Substring that matches 1st group
assert(m[2].str() == "2"); // Substring that matches 2nd group
assert(m.prefix().str() == "ABC:1-> "); // All before 1st character match
assert(m.suffix().str() == ";; XYZ:3<<<"); // All after last character match
// for (string &&str : m) { // Alternatively. You can also do
// cout << str << endl;
// }
}
return EXIT_SUCCESS;
}
smatch
— это специализация std::match_results, которая хранит информацию о найденных совпадениях (матчах).
Пример std::regex_match
Лаконичный и приятный пример, который вы всегда можете найти в каждой книге о регулярках, — это валидация электронной почты. И именно здесь идеально подходит функция std::regexmatch
.
bool is_valid_email_id(string_view str) {
static const regex r(R"(w+@w+.(?:com|in))");
return regex_match(str.data(), r);
}
int main() {
assert(is_valid_email_id("vishalchovatiya@ymail.com") == true);
assert(is_valid_email_id("@abc.com") == false);
return EXIT_SUCCESS;
}
return EXIT¨C14Cmatch
, а не std::regex¨C15Cmatch
сопоставляет (матчит) всю входную последовательность.
Еще одна примечательная вещь — это статический объект регулярного выражения (static const regex), чтобы избежать создания («компиляции/интерпретации») нового объекта регулярного выражения каждый раз при заходе в функцию.
Вся ирония этого крошечного фрагмента кода заключается в том, что он генерирует порядка 30 тысяч строк сборки с флагом -O3. И это просто смешно. Но не волнуйтесь, это уже было доведено до комитета ISO C++. И в скором времени мы можем получить обновление, устраняющее проблему. Между тем у нас есть и другие альтернативы (упомянутые в конце этой статьи).
Разница между std::regex_match и std::regex_search
Вам может быть интересно, почему у нас есть две функции, выполняющие почти одинаковую работу? Даже я изначально не понимал этого. Но после многократного прочтения описания, предоставляемого cppreference, я нашел ответ. И чтобы объяснить этот ответ, я создал пример (очевидно, не без помощи StackOverflow):
int main() {
const string input = "ABC:1-> PQR:2;;; XYZ:3<<<"s;
const regex r(R"((w+):(w+);)");
smatch m;
assert(regex_match(input, m, r) == false);
assert(regex_search(input, m, r) == true && m.ready() == true && m[1] == "PQR");
return EXIT_SUCCESS;
}
std::regexmatch
возвращает true
только тогда, когда совпадает вся входная последовательность, в то время как std::regexsearch
вернет true
, даже если только часть последовательности соответствует регулярному выражению.
Пример std::regex_iterator
std::regex_iterator
полезен, когда требуется очень подробная информация о соответствиях и сабматчах.
#define C_ALL(X) cbegin(X), cend(X)
int main() {
const string input = "ABC:1-> PQR:2;;; XYZ:3<<<"s;
const regex r(R"((w+):(d))");
const vector<smatch> matches{
sregex_iterator{C_ALL(input), r},
sregex_iterator{}
};
assert(matches[0].str(0) == "ABC:1"
&& matches[0].str(1) == "ABC"
&& matches[0].str(2) == "1");
assert(matches[1].str(0) == "PQR:2"
&& matches[1].str(1) == "PQR"
&& matches[1].str(2) == "2");
assert(matches[2].str(0) == "XYZ:3"
&& matches[2].str(1) == "XYZ"
&& matches[2].str(2) == "3");
return EXIT_SUCCESS;
}
Ранее (в C++11) существовало ограничение, заключающееся в том, что std::regex_interator
нельзя было вызывать с временным объектом регулярного выражения. Что было исправлено с помощью перегрузки в C++14.
Пример std::regex_token_iterator
std::regextokeniterator
— это утилита, которую вы будете использовать в 80% случаев. Она немного отличается от std::regexiterator
. Разница между td::regexiterator
и std::regextokeniterator
заключается в том, что
-
std::regexiterator
указывает на соответствующие результаты. -
std::regextokeniterator
указывает на сабматчи.
В std::regextoken_iterator
каждый итератор содержит только один соответствующий результат.
#define C_ALL(X) cbegin(X), cend(X)
int main() {
const string input = "ABC:1-> PQR:2;;; XYZ:3<<<"s;
const regex r(R"((w+):(d))");
// Note: vector<string> here, unlike vector<smatch> as in std::regex_iterator
const vector<string> full_match{
sregex_token_iterator{C_ALL(input), r, 0}, // Mark `0` here i.e. whole regex match
sregex_token_iterator{}
};
assert((full_match == decltype(full_match){"ABC:1", "PQR:2", "XYZ:3"}));
const vector<string> cptr_grp_1st{
sregex_token_iterator{C_ALL(input), r, 1}, // Mark `1` here i.e. 1st capture group
sregex_token_iterator{}
};
assert((cptr_grp_1st == decltype(cptr_grp_1st){"ABC", "PQR", "XYZ"}));
const vector<string> cptr_grp_2nd{
sregex_token_iterator{C_ALL(input), r, 2}, // Mark `2` here i.e. 2nd capture group
sregex_token_iterator{}
};
assert((cptr_grp_2nd == decltype(cptr_grp_2nd){"1", "2", "3"}));
return EXIT_SUCCESS;
}
Инверсия соответствия с std::regex_token_iterator
#define C_ALL(X) cbegin(X), cend(X)
int main() {
const string input = "ABC:1-> PQR:2;;; XYZ:3<<<"s;
const regex r(R"((w+):(d))");
const vector<string> inverted{
sregex_token_iterator{C_ALL(input), r, -1}, // `-1` = parts that are not matched
sregex_token_iterator{}
};
assert((inverted == decltype(inverted){
"",
"-> ",
";;; ",
"<<<",
}));
return EXIT_SUCCESS;
}
Пример std::regex_replace
string transform_pair(string_view text, regex_constants::match_flag_type f = {}) {
static const auto r = regex(R"((w+):(d))");
return regex_replace(text.data(), r, "$2", f);
}
int main() {
assert(transform_pair("ABC:1, PQR:2"s) == "1, 2"s);
// Things that aren't matched are not copied
assert(transform_pair("ABC:1, PQR:2"s, regex_constants::format_no_copy) == "12"s);
return EXIT_SUCCESS;
}
Вы видите, что во втором вызове transformpair
мы передали флаг std::regexconstants::formatnocopy
, который говорит не копировать те части, которые не соответствуют регулярке. В std::regexconstant есть много подобных полезных флагов.
Кроме того, мы создали новую строку, содержащую результаты. Но что, если нам не нужна новая строка, а нужно добавить результаты куда-нибудь, возможно, в контейнер, поток или уже существующую строку. Угадайте, что! Стандартная библиотека уже реализует такую потребность также с помощью перегруженного std::regexreplace
следующим образом:
int main() {
const string input = "ABC:1-> PQR:2;;; XYZ:3<<<"s;
const regex r(R"(-|>|<|;| )");
// Prints "ABC:1 PQR:2 XYZ:3 "
regex_replace(ostreambuf_iterator<char>(cout), C_ALL(input), r, " ");
return EXIT_SUCCESS;
}
Примеры использования
Разделение строки с помощью разделителя (delimiter)
std::strtok
является наиболее подходящим и оптимальным кандидатом для такой задачи, но в целях демонстрации продемонстрируем как это можно сделать с помощью регулярного выражения:
#define C_ALL(X) cbegin(X), cend(X)
vector<string> split(const string& str, string_view pattern) {
const auto r = regex(pattern.data());
return vector<string>{
sregex_token_iterator(C_ALL(str), r, -1),
sregex_token_iterator()
};
}
int main() {
assert((split("/root/home/vishal", "/")
== vector<string>{"", "root", "home", "vishal"}));
return EXIT_SUCCESS;
}
Удаление пробелов из строки
string trim(string_view text) {
static const auto r = regex(R"(s+)");
return regex_replace(text.data(), r, "");
}
int main() {
assert(trim("12 3 4 5"s) == "12345"s);
return EXIT_SUCCESS;
}
Поиск строк, содержащих или НЕ содержащих определенные слова, из файла
string join(const vector<string>& words, const string& delimiter) {
return accumulate(next(begin(words)), end(words), words[0],
[&delimiter](string& p, const string& word)
{
return p + delimiter + word;
});
}
vector<string> lines_containing(const string& file, const vector<string>& words) {
auto prefix = "^.*?\b("s;
auto suffix = ")\b.*$"s;
// ^.*?b(one|two|three)b.*$
const auto pattern = move(prefix) + join(words, "|") + move(suffix);
ifstream infile(file);
vector<string> result;
for (string line; getline(infile, line);) {
if(regex_match(line, regex(pattern))) {
result.emplace_back(move(line));
}
}
return result;
}
int main() {
assert((lines_containing("test.txt", {"one","two"})
== vector<string>{"This is one",
"This is two"}));
return EXIT_SUCCESS;
}
/* test.txt
This is one
This is two
This is three
This is four
*/
То же самое касается поиска строк, которые не содержат слов с шаблоном ^((?!(one|two|three)).)*$
.
Поиск файлов в папке
namespace fs = std::filesystem;
vector<fs::directory_entry> find_files(const fs::path &path, string_view rg) {
vector<fs::directory_entry> result;
regex r(rg.data());
copy_if(
fs::recursive_directory_iterator(path),
fs::recursive_directory_iterator(),
back_inserter(result),
[&r](const fs::directory_entry &entry) {
return fs::is_regular_file(entry.path()) &&
regex_match(entry.path().filename().string(), r);
});
return result;
}
int main() {
const auto dir = fs::temp_directory_path();
const auto pattern = R"(w+.png)";
const auto result = find_files(fs::current_path(), pattern);
for (const auto &entry : result) {
cout << entry.path().string() << endl;
}
return EXIT_SUCCESS;
}
Общие советы по использованию регулярных выражений
-
Для описания шаблона регулярного выражения в C++ лучше используйте сырые строковые литералы.
-
Пользуйтесь инструментом проверки регулярных выражений, например https://regex101.com. Что мне нравится в regex101, так это функция генерации кода и вычисление затраченного времени (будет полезна при оптимизации регулярного выражения).
-
Кроме того, хорошим тоном будет добавление объяснения, сгенерированного инструментом проверки, в виде комментария над шаблоном регулярного выражения в вашем коде.
Производительность:
-
Если вы используете чередование (alternation), попробуйте расположить параметры в порядке наивысшей вероятности, например
com|net|org
.
-
Старайтесь использовать ленивые квантификаторы.
-
По возможности используйте группы без захвата.
-
Отключайте бэктрекинг.
-
Использование отрицательного класса символов более эффективно, чем использование ленивой точки.
-
Заключение
Дело не в том, будете ли вы использовать регулярные выражения исключительно на C++ или любым другим языком. Я сам использую их в основном в IDE (в vscode для анализа файлов логов) и на терминале Linux. Имейте в виду, что чрезмерное использование регулярных выражений дает чрезмерное ощущение собственной сообразительности. И это отличный способ рассердить на вас ваших коллег (и всех, кому нужно работать с вашим кодом). Кроме того, регулярные выражения являются излишними для большинства задач синтаксического анализа, с которыми вы столкнетесь в своей повседневной работе.
Регулярные выражения действительно подходят для сложных задач, где рукописный код синтаксического анализа в любом случае будет столь же медленным; и для чрезвычайно простых задач, когда удобочитаемость и надежность регулярных выражений перевешивают их затраты на производительность.
Еще одна примечательная вещь — текущая реализация регулярных выражений (до 19 июня 2020 года) в стандартных библиотеках имеет проблемы с производительностью и раздутием кода. Так что выбирайте с умом между версиями библиотек Boost, CTRE и Std. Скорее всего, вы согласитесь с работой Ханы Дусиковой над регулярным выражением времени компиляции. Кроме того, ее выступление на CppCon в 2018 и 2019 было бы для вас полезно, особенно если вы планируете использовать регулярное выражение во встроенных системах.
Узнать подробнее о курсе «C++ Developer. Professional»
Записаться на открытый урок по теме «Backend на современном C++»
ЗАБРАТЬ СКИДКУ