Как обрабатывать или исключать исключения из С++ 11 <regex> соответствия функций (§28.11)?
Начиная с С++ 11 заголовки <regex>
определяют функции std::regex_match
, std::regex_search
и std::regex_replace
в §28.11. Я думаю, что есть веская причина для того, чтобы эти функции не были noexcept
, но я не мог найти никакой ссылки о том, что они могут бросить или почему.
- Какие типы исключений могут вызывать эти функции?
- Какие условия выполнения приводят к выбросам этих исключений?
- Соответствует ли стандарт, что для некоторых наборов аргументов эти функции никогда не бросаются, например. гарантирует, что
regex_match(anyString, regex("."))
никогда не выбрасывает?
PS: Поскольку некоторые из этих исключений, вероятно, наследуются от std::runtime_error
, они могут бросать std::bad_alloc
во время их построения.
Ответы
Ответ 1
С++ 11 §28.6 утверждает
Класс regex_error
определяет тип объектов, которые были выбраны как исключения для сообщения об ошибках из библиотеки регулярных выражений.
Это означает, что библиотека <regex>
не должна бросать что-либо еще отдельно. Вы правы, что построение regex_error
, которое наследуется от runtime_error
, может вызывать bad_alloc
во время построения из-за нехватки памяти, поэтому вы также должны проверить это в коде обработки ошибок. К сожалению, это не позволяет определить, какая конструкция regex_error
действительно выбрасывает bad_alloc
.
Для алгоритмов регулярных выражений в § 28.11 в §28.11.1 указано, что
Алгоритмы, описанные в этом подпункте, могут вызывать исключение типа regex_error
. Если выбрано такое исключение e
, e.code()
возвращает либо regex_constants::error_complexity
, либо regex_-constants::error_stack
.
Это означает, что если функции в § 28.11 когда-либо бросают a regex_error
, он должен придерживаться одного из этих кодов и ничего другого. Однако обратите внимание также на то, что вещи, которые вы передаете библиотеке <regex>
, такие как распределители и т.д., Также могут выдавать, например. распределитель match_results
, который может запускаться, если результаты будут добавлены в данный контейнер match_results
. Также обратите внимание, что в § 28.11 имеются сокращенные функции, которые "как бы" конструируют match_results
, такие как
template <class BidirectionalIterator, class charT, class traits>
bool regex_match(BidirectionalIterator first, BidirectionalIterator last,
const basic_regex<charT, traits> & e,
regex_constants::match_flag_type flags =
regex_constants::match_default);
template <class BidirectionalIterator, class charT, class traits>
bool regex_search(BidirectionalIterator first, BidirectionalIterator last,
const basic_regex<charT, traits> & e,
regex_constants::match_flag_type flags =
regex_constants::match_default);
и, возможно, другие. Поскольку они могут создавать и использовать match_results
со стандартным allocator
внутри, они могут бросать что-либо std::allocator
throws. Поэтому ваш простой пример regex_match(anyString, regex("."))
может также зависеть от конструкции и использования распределителя по умолчанию.
Еще одно предостережение, что для некоторых функций и классов <regex>
в настоящее время невозможно определить, был ли какой-либо блок bad_alloc
выбран каким-либо распределителем или во время построения исключения regex_error
.
В общем, если вам нужно что-то с лучшими исключениями, избегайте использования <regex>
. Если вам требуется простое сопоставление шаблонов, вам лучше сворачивать собственные функции безопасного соответствия/поиска/замены, потому что невозможно ограничить ваши регулярные выражения, чтобы избежать этих исключений в переносном или перепрограммированном виде, даже используя пустое регулярное выражение ""
может дать вам исключение.
PS: Обратите внимание, что стандарт С++ 11 довольно плохо написан в некоторых аспектах, не имея полной перекрестной ссылки. Например. нет явного уведомления в соответствии с предложениями методов match_results
, чтобы выбросить что-либо, в то время как §28.10.1.1 говорится (основное внимание):
Во всех конструкторах match_results
копия аргумента allocator
должна использоваться для любого выполняемого выделения памяти конструктором или функциями-членами в течение всего времени жизни объект.
Так что будьте внимательны при просмотре стандартов, как юрист!; -)
Ответ 2
regex_error
- единственное исключение, упомянутое как брошенное из любого из классов или алгоритмов в <regex>
. Существуют две основные категории ошибок: неправильные регулярные выражения и неспособность обработать соответствие.
Конструкторы для basic_regex
могут вызывать regex_error
(согласно [re.regex.construct] \3, \7, \14 и \17), если переданный аргумент (или последовательность) является "недействительным регулярным выражением". То же самое верно, если вы попытаетесь присвоить basic_regex
недопустимое регулярное выражение ([re.regex.assign]/15).
Отдельно от этого алгоритмы также могут бросать regex_error
([re.except]/1):
Функции, описанные в этом отчете, сообщают об ошибках путем исключения исключений типа regex_error
. Если выбрано такое исключение e
, e.code()
возвращает либо regex_constants::error_complexity
, либо regex_constants::error_stack
.
где эти два кода ошибок означают ([re.err]):
error_complexity
: сложность попытки совпадения с регулярным выражением превышала заданный уровень.
error_stack
: Недостаточно памяти, чтобы определить, может ли регулярное выражение соответствовать указанной последовательности символов.
Ответ 3
Я считаю, что это то, что вы должны обрабатывать.
Для компиляции есть 3 исключения.
Для поиска/сопоставления/замены вам, вероятно, нужно всего лишь обрабатывать 2.
Btw, если вы не обрабатываете исключения так, как описано ниже, то ваш
код будет летать слепым и не предназначен для потребления человеком.
std::regex Regex;
bool CompileRegex( std::string& strRx, unsigned int rxFlags )
{
try
{
Regex.assign( strRx, rxFlags );
}
catch ( std::regex_error & e )
{
// handle e
return false;
}
catch ( std::out_of_range & e )
{
// handle e
return false;
}
catch ( std::runtime_error & e )
{
// handle e
return false;
}
return true;
}
bool UseRegex( std::string& strSource, std::string& strOut, std::string strReplace )
{
try
{
if ( std::regex::regex_search( strSource, _match, Regex )
{}
// or
if ( strOut = std::regex::regex_replace( strSource, Regex, strReplace ) )
{}
}
catch ( std::out_of_range & e )
{
// handle e
return false;
}
catch ( std::runtime_error & e )
{
// handle e
return false;
}
return true;
}
Ответ 4
Эта ссылка здесь может помочь. Как вы можете видеть, большинство из них относятся к недопустимому регулярному выражению, а не к недопустимым входам (которые должны и не бросать никаких ошибок, они просто не совпадают.
Просматривая здесь, я вижу, что конструктор regex_replace и regex может вызывать одно из типов исключения regex_error. Я также видел некоторые связанные с памятью исключения, но, как сказано, это время выполнения и может быть выброшено из любой части кода. Поскольку документация не предоставляет ничего другого, единственный способ узнать это будет из самого кода.
Ответ 5
См. pp735-6 от Josuttis "Стандартная библиотека С++" 2-е издание. Здесь список исключений, каждый с текстовым пояснением на следующих двух строках
std::regex_constants::error_collate:
"error_collate: "
"regex has invalid collating element name";
std::regex_constants::error_ctype:
"error_ctype: "
"regex has invalid character class name";
std::regex_constants::error_escape:
"error_escape: "
"regex has invalid escaped char. or trailing escape";
std::regex_constants::error_backref:
"error_backref: "
"regex has invalid back reference";
std::regex_constants::error_brack:
"error_brack: "
"regex has mismatched ’[’ and ’]’";
std::regex_constants::error_paren:
"error_paren: "
"regex has mismatched ’(’ and ’)’";
std::regex_constants::error_brace:
"error_brace: "
"regex has mismatched ’{’ and ’}’";
std::regex_constants::error_badbrace:
"error_badbrace: "
"regex has invalid range in {} expression";
std::regex_constants::error_range:
"error_range: "
"regex has invalid character range, such as ’[b-a]’";
std::regex_constants::error_space:
"error_space: "
"insufficient memory to convert regex into finite state";
std::regex_constants::error_badrepeat:
"error_badrepeat: "
"one of *?+{ not preceded by valid regex";
std::regex_constants::error_complexity:
"error_complexity: "
"complexity of match against regex over pre-set level";
std::regex_constants::error_stack:
"error_stack: "
"insufficient memory to determine regex match";