Регулярное выражение, вызывающее переполнение стека

В дополнение к моему предыдущему вопросу: регулярное выражение ECMAScript для многолинейной строки я реализовал следующую процедуру погрузки:

void Load( const std::string& szFileName )
{
     static const std::regex regexObject( "=== ([^=]+) ===n((?:.|n)*)n=== END 1 ===", std::regex_constants::ECMAScript | std::regex_constants::optimize );
     static const std::regex regexData( "<([^>]+)>:([^<]*)n", std::regex_constants::ECMAScript | std::regex_constants::optimize );

     std::ifstream inFile( szFileName );
     inFile.exceptions( std::ifstream::badbit );

     std::string szFileData( (std::istreambuf_iterator<char>(inFile)), (std::istreambuf_iterator<char>()) );

     inFile.close();

     std::vector<std::future<void>> vecFutures;

     for( std::sregex_iterator itObject( szFileData.cbegin(), szFileData.cend(), regexObject ), end; itObject != end; ++itObject )
     {
          if( (*itObject)[1] == "OBJECT1" )
          {
               vecFutures.emplace_back( std::async( []( std::string szDataString ) {
                    for( std::sregex_iterator itData( szDataString.cbegin(), szDataString.cend(), regexData ) { // Do Stuff }
               }, (*itObject)[2].str() ) );
          }
          else if( (*itObject)[1] == "OBJECT2" )
          {
               vecFutures.emplace_back( std::async( []( std::string szDataString ) {
                    for( std::sregex_iterator itData( szDataString.cbegin(), szDataString.cend(), regexData ) { // Do Stuff }
               }, (*itObject)[2].str() ) );
          }
     }

     for( auto& future : vecFutures )
     {
          future.get();
     }
}

однако загрузка его с этим файлом приводит к переполнению стека (параметры: 0x00000001, 0x00332FE4):

=== OBJECT2 ===
<Name>:Test Manufacturer
<Supplier>:Test Supplier
<Address>:Test Multiline
Contact
Address
<Email>:test@test.co.uk
<Telephone Number>:0123456789
=== END OBJECT2 ===
=== OBJECT1 ===
<Number>:1
<Name>:Test
<Location>:Here
<Manufacturer>:
<Model Number>:12345
<Serial Number>:54321
<Owner>:Me
<IP Address>:0.0.0.0
=== END OBJECT1 ===

Я не смог найти источник переполнения стека, но он выглядит как внешний std::sregex_iterator loop несет ответственность.

спасибо заранее!

4 ответов


вот еще одна попытка:

=== ([^=]+) ===\n((?:(?!===)[^\n]+\n)+)=== END  ===

в вашем C++ это, очевидно, будет написано как:

=== ([^=]+) ===\n((?:(?!===)[^\n]+\n)+)=== END \1 ===

это сделано для минимального отступления (по крайней мере, при сопоставлении), хотя я немного Мистер усталое лицо на данный момент, поэтому, вероятно, пропустил довольно много способов улучшить его.

здесь два предположения, которые используются, чтобы избежать много сил (что, возможно, вызывает переполнение стека, как и другие сказал):

  1. что никогда не бывает === в начале строки, за исключением линий маркера начала/конца.
  2. что C++ поддерживает эти функции regex-в частности, использование отрицательного lookahead (?!). Это должно, учитывая, что это диалект ECMAScript.

пояснил:

=== ([^=]+) ===\n

сопоставьте и захватите маркер начала объекта. The [^=] один из способов избежать относительно небольшого количества отступления здесь, как и у вас - мы не используем [^ ], потому что я не знаю, могут ли быть пробелы в идентификаторе объекта.

((?:

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

   (?!===)

отрицательный lookahead-мы не хотим === в начале нашей захваченной линии.

   [^\n]+\n

соответствует одной строке в отдельности.

)+)

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

=== END  ===

сопоставьте маркер конца.

сравнение (с помощью RegexBuddy):

оригинальная версия:

  • первый матч: 1277 шагов
  • неудачный матч: 1 шаг (это связано с разрывом линии между объектами)
  • второй матч: 396 шаги

каждый добавленный объект вызовет количество шагов расти для предыдущих. Например, добавление еще одного объекта (копия объекта 2, переименованного в 3) приведет к: 2203 шага, 1322 шага, 425 шагов.

такой вариант:

  • первый матч: 67 шагов
  • неудачный матч: 1 шаг (еще раз из-за разрыва линии между объектами)
  • второй матч: 72 шага
  • неудачный матч: 1 шаг
  • третий матч: 67 шагов

святое катастрофическое отступление. Виновник -(?:.|\n)*. Всякий раз, когда вы видите такую конструкцию, вы знаете, что напрашиваетесь на неприятности.

почему? Потому что вы говорите движку соответствовать любому символу (кроме newline) или newline, столько раз, сколько возможно, или нет. Позволь мне провести тебя через это.

двигатель запустится, как ожидалось, и соответствует === OBJECT2 === - часть без каких-либо серьезных проблем, новая строка будет потребляться, и тогда начнется ад. Двигатель потребляет Все, вплоть до === END OBJECT1 ===, и вернуться свой путь оттуда к подходящему матчу. Backtracking в основном означает возврат на один шаг назад и повторное применение регулярного выражения, чтобы увидеть, работает ли оно. В основном, пробуя все возможные перестановки с вашей строкой. Это, в вашем случае, приведет к нескольким сто тысяч попытки. Наверное, поэтому у тебя проблемы с вещами.

Я не знаю, лучше ли ваш код или если в нем есть какие-либо ошибки, но (?:.|\n)* то же самое что писать .* С *s*модификатор ingle line (точка соответствует новым строкам) или [\S\s]*. Если вы замените эту конструкцию одним из двух, которые я рекомендовал, вы, надеюсь, больше не увидите ошибку переполнения стека.

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


ваши выражения, похоже, вызывают много отступлений. Я бы изменил ваши выражения на:

первый: ^===\s+(.*?)\s+===[\r\n]+^(.*?)[\r\n]+^===\s+END\s+\s+===

второй: ^<([^>]+)>:([^<]*)

оба эти выражения работают с параметрами: Multiline и dotmatchesall. От включая начало линии anchor ^ это ограничивает перебор на одной линии или одна группа.


попробуйте вместо этого использовать этот шаблон:

static const std::regex regexObject( "=== (\S+) ===\n((?:[^\n]+|\n(?!=== END \1 ===))*)\n=== END \1 ===", std::regex_constants::ECMAScript | std::regex_constants::optimize );