Как обрабатывать или исключать исключения из С++ 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";