正则表达式中的回溯导致的堆栈溢出

时间:2015-04-09 08:27:52

标签: c++ regex c++11 stack-overflow backtracking

我想使用以下正则表达式使用std :: regex与C ++ 11匹配一个字符串:

([^;]|'[^']*')*

我想要实现的目标如下:我想匹配除分号以外的所有字符,但是,如果用撇号括起来,则应忽略分号(这表示字符串)。当尝试应用正则表达式时,我得到堆栈溢出。我意识到问题是由excessive backtracking引起的。不幸的是,我不知道如何摆脱这个问题。如何重写表达式,使其不会导致回溯负载?

带有虚拟文本的最小代码示例:

#include <regex>
#include <string>
int main()
{
    std::string str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut suscipit enim. Praesent varius purus ac sem vulputate pulvinar. Mauris scelerisque arcu tortor, at bibendum dui rhoncus ut. Nunc dictum malesuada condimentum. Mauris ornare nunc eget efficitur tempor. Morbi ex nibh, consectetur vitae bibendum id, bibendum varius purus. Proin finibus quam vel ornare molestie. Mauris condimentum nisi efficitur, fringilla massa ut, commodo diam. Mauris lobortis laoreet magna sed commodo. Donec faucibus lectus placerat ex pulvinar interdum.";
    std::regex rgx("([^;]|'[^']*')*");
    std::regex_match(std::begin(str), std::end(str), rgx); // Stack overflow!
    return 0;
}

我正在使用Visual Studio 2012。

1 个答案:

答案 0 :(得分:2)

如果你将正则表达式语法切换到基本posix而不是deafult ECMAscript,它将不再溢出:

std::regex rgx("([^;]|'[^']*')*", std::regex_constants::basic);

我使用MSVC2013进行了测试,它确实有效。不幸的是,它也不符合您的期望,正如这个小变体所示:

...
std::smatch cm;
std::regex_search(str, cm, rgx, std::regex_constants::match_any | std::regex_constants::match_continuous | std::regex_constants::match_not_null); // No overflow!
for (int i = 0; i < cm.size(); i++)
    std::cout << "Found:" << cm[i];  // but no result is printed out. 

如果选择std::regex_constants::extended选项,堆栈溢出将再次返回。

如果您使用 RegExr 上的数据测试正则表达式,您将了解问题:

enter image description here

正则表达式导致无限的潜在匹配。一旦减少了潜在的匹配集,例如在;之后放置Lorem,就不再有堆栈溢出。

这确实是非常不幸的:没有标准匹配选项(例如std::regex_constants::match_any | std::regex_constants::match_continuous | std::regex_constants::match_not_null)似乎可以解决这个常见问题(例如:没有&#34;匹配最大的&#34;)。

尽管如此,它仍然与标准库的MSVC实现有关。这个online GCC example运行正常,表达方式相同。因此,提升 RE2 替代方案值得考虑。

此代码在MSVC2013上使用boost 1.57,运行没有任何问题。如您所见,它使用与标准相同的代码/名称,但将std替换为boost

#include <iostream>
#include <boost/regex.hpp>  // instead of <regex>
#include <string>
                            // use the boost alternative to std
using namespace boost::regex_constants;  
using boost::regex; using boost::smatch; using boost::regex_search;

int main()
{
    std::string str = "your very long string..." ;
    regex rgx("([^;]|'[^']*')*", extended | nosubs);
    smatch cm;
    //regex_match(str, cm, rgx, regex_constants:: match_continuous | match_not_null); // Stack overflow!
    regex_search(str, cm, rgx, match_continuous | match_not_null); 
    for (int i = 0; i < cm.size(); i++)
        std::cout << "Found:" << cm[i];
    return 0;
}