2016-03-30 2 views
13

Я разборщик stackoverflow дампа и подошел к this, казалось бы, невинный вопрос с маленькой, почти невидимой деталью, что у него 22311 пробелов в конце текста.С ++ regex segfault на длинных последовательностях

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

std::regex space_regex("\\s+", std::regex::optimize); 
... 
std::regex_replace(out, in, in + strlen(in), space_regex, " "); 

SIGSEGV показывает, и у меня есть начал расследование.

код теста:

#include <regex> 
... 
std::regex r("\\s+", std::regex::optimize); 
const char* bomb2 = "Small text\n\nwith several\n\nlines."; 
std::string test(bomb2); 
for (auto i = 0; i < N; ++i) test += " "; 

std::string out = std::regex_replace(test.c_str(), r, " "); 
std::cout << out << std::endl; 

для (GCC 5.3.0)

$ g++ -O3 -std=c++14 regex-test.cpp -o regex-test.out 

максимум N перед тем SIGSEGV показывает вверх является 21818 (для этой конкретной строки), а также для

$ g++ -O0 -std=c++14 regex-test.cpp -o regex-test.out 

его 12180.

«Хорошо, давайте попробуем заговорить, это тренда и цель заменить gcc» - никогда бы я не ошибся. С -O0 clang (v. 3.7.1) падает на 9696 пробелов - меньше, чем gcc, но не очень, но с -O3 и даже с -O2 он падает на пространства ZERO.

Краш свалка представляет огромные stacktraces (35K кадров) рекурсивных вызовов

std::__detail::_Executor<char*, std::allocator<std::__cxx11::sub_match<char*> >, std::__cxx11::regex_traits<char>, true>::_M_dfs 

Вопрос 1: Является ли это ошибка? Если да, я должен сообщить об этом?

Вопрос 2: Есть ли способ преодолеть проблему (кроме увеличения размера системного стека, попытки других библиотек регулярных выражений и записи собственных функций для замены пробелов)?


Поправка:bug report создан для libstdC++

+6

Ваш тестовый код [корректно работает с лязгом, если опция компилятора -stdlib = LibC++ используется] (http://coliru.stacked-crooked.com/a/91ffcf96e925b2a3). Скорее всего, это ошибка в libstdC++ – cpplearner

ответ

8

Является ли это ошибка? Если да, я должен сообщить об этом?

Да, это ошибка.

cout << '"' << regex_replace("Small text\n\nwith several\n\nlines." + string(22311, ' '), regex("\\s+", regex::optimize), " ") << '"' << endl; 

Но это всего лишь ошибка в libstdC++, поэтому не стесняйтесь сообщить об этом здесь: https://gcc.gnu.org/bugzilla/buglist.cgi?product=gcc&component=libstdc%2B%2B&resolution=---

Есть ли умный способ преодолеть проблему?

Если вы запрашиваете новый regex, который работает, я пробовал несколько разных версий, и все они не работают на libstdC++, поэтому я бы сказал, если вы хотите использовать regex для решения этой проблемы , вам нужно скомпилировать файл libC++.

Но, если честно, если вы используете regex раздеться дубликатом белого пространства, "Now you have two problems"

Лучшим решение может использовать adjacent_find который runs fine with libstdc++ as well:

const auto func = [](const char a, const char b){ return isspace(a) && isspace(b); }; 

for(auto it = adjacent_find(begin(test), end(test), func); it != end(test); it = adjacent_find(it, end(test), func)) { 
    *it = ' '; 
    it = test.erase(next(it), find_if_not(next(it), end(test), [](const auto& i) { return isspace(i); })); 
} 

Это будет возвращать то же самое ваш regex бы:

«Малый текст с несколькими строками».

Но если вы собираетесь для простоты, вы можете также использовать unique:

test.resize(distance(test.begin(), unique(test.begin(), test.end(), [](const auto& a, const auto& b) { return isspace(a) && isspace(b); }))); 

который будет возвращать:

«Малый текст
с несколькими
линий. "

+1

Это было в конвейере регулярных выражений, поэтому выбор был несколько естественным. Спасибо, что указали на 'neighbour_find', STL меня удивляет скрытыми сокровищами. – MadRunner

+0

@MadRunner Я бы согласился на этот момент, если вы уже * должны * использовать 'regex' переводит дополнительные функции в это отличное решение. Но я предполагаю, что в этом случае это будет означать использование libC++ :( –

+0

@MadRunner Кстати, я не хотел писать эту ошибку, потому что я не хотел, чтобы моя электронная почта быть публичной информацией, но если вы напишете ее и захотите указать номер ошибки в комментарии здесь или добавить к вашему ответу вероятно, будет полезным для будущих поколений. –

0

Вопрос 2 (умный способ преодоления проблемы)

не очень умный, но ... вы можете итерацию на ограниченный заменить.

Пример

#include <regex> 
#include <iostream> 

int main() 
{ 
    constexpr int N = 22311; 

    //std::regex r("\\s+"); 
    std::regex r("\\s{2,100}"); 

    const char* bomb2 = "Small text\n\nwith several\n\nlines."; 

    std::string test(bomb2); 

    for (auto i = 0; i < N; ++i) 
     test += " "; 

    std::string out = test; 

    std::size_t preSize; 

    do 
    { 
     preSize = out.size(); 

     out = std::regex_replace(out, r, " "); 
    } 
    while (out.size() < preSize); 

    std::cout << '\"' << out << '\"' << std::endl; 

    return 0; 
} 
Смежные вопросы