【问题标题】:Microsoft's implementation of std::regex微软对 std::regex 的实现
【发布时间】:2013-07-12 04:06:15
【问题描述】:

我一直在使用 <regex> 库(Microsoft Visual Studio 2012:更新 3),尝试使用它为我的应用程序实现稍微安全一点的加载过程,并且遇到了一些初期困难(参见 @@ 987654321@、Concurrently using std::regex, defined behaviour?ECMAScript Regex for a multilined string)。

通过使用建议的正则表达式here,我已经解决了我最初的麻烦(导致堆栈溢出等),并且运行良好;但是,如果我的文件太大,那么它会导致堆栈溢出(我通过增加堆栈提交和保留大小来规避),或者如果堆栈大小足够大而不会导致堆栈溢出,那么它会导致 @987654326 @ 错误代码为12 (error_stack)

这里有一个独立的例子来复制这个问题:

#include <iostream>
#include <string>
#include <regex>

std::string szTest = "=== TEST1 ===\n<Example1>:Test Data\n<Example2>:More Test Data\n<Example3>:Test\nMultiline\nData\n<Example4>:test_email@test.com\n<Example5>:0123456789\n=== END TEST1 ===\n=== TEST2 ===\n<Example1>:Test Data 2\n<Example2>:More Test Data 2\n<Example3>:Test\nMultiline\nData\n2\n<Example4>:test_email2@test.com\n=== END TEST2 ===\n=== TEST3 ===\n<Example1>:Random Test Data\n<Example 2>:More Random Test Data\n<Example 3>:Some\nMultiline\nRandom\nStuff\n=== END TEST3 ===\n\
                      === TEST1 ===\n<Example1>:Test Data (Second)\n<Example2>:Even More Test Data\n<Example3>:0123456431\n=== END TEST1 ===";

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

    for( std::sregex_iterator itObject( szTest.cbegin(), szTest.cend(), regexObject ), end; itObject != end; ++itObject )
    {
        std::cout << "Type: " << (*itObject)[1].str() << std::endl;
        std::cout << "Data: " << (*itObject)[2].str() << std::endl;

        std::cout << "-------------------------------------" << std::endl;
    }
}

使用默认堆栈大小(4kB 提交和 1MB 保留)编译它会导致抛出堆栈溢出异常;并且在更改堆栈大小(8kB 提交和 2MB 保留)时,它会导致 std::regex_error 被抛出,错误代码为 12 (error_stack)

我可以做些什么来防止这些错误,或者仅仅是正则表达式库被设计为仅用于小字符串(即 DoB 检查等)?

提前致谢!

【问题讨论】:

    标签: c++ regex c++11 stack-overflow standard-library


    【解决方案1】:

    忘记&lt;regex&gt;——至少现在是这样,可能是永远。在我看来,规范已损坏且无法使用;但即使不是,至少当前的实现是,并且可能会在未来几年内实现。

    这是因为所有主要供应商都从头开始实施自己的正则表达式引擎,而不是依赖现有的、经过尝试和测试的库。这是一项巨大的努力。

    我的建议:现在使用另一个正则表达式库,并给&lt;regex&gt; 一个宽阔的泊位。替代方案是 Boost.RegexBoost.Xpressive 和(C 风格)库,例如 PCREOniguruma

    顺便说一句,我们今天在聊天中讨论了这个话题。如果你有半个小时的时间,you can read my detailed rant and some interesting counter-points

    【讨论】:

    • [需要引用]、[需要引用]、[需要引用]和[需要引用]。我也推荐 Boost 正则表达式
    • @sehe 好吧,看聊天。此外,“在我看来”,虽然肯定是一种逃避,但在某种程度上排除了引用的需要。
    • 我在那儿,记得。此外,“IMO”并不明确适用于所有索赔。事实上,“但即使不是”似乎明确地删除了该子句对其后面的声明的影响。
    【解决方案2】:

    问题在于反向引用 (\1)。反向引用是邪恶的,或者至少在一般情况下很难实现,并且不容易识别非一般情况。

    在您的情况下,问题在于正则表达式的第一个匹配项将从第一个 === TEST1 ===最后一个 === END TEST1 ===。这不是你想要的,但它是正则表达式的工作方式。 (“最长的最左边规则”。)理论上,仍然可以匹配正则表达式而不杀死堆栈,但我怀疑您使用的正则表达式库是否足够聪明以进行优化。

    您可以通过使数据部分 (((?:.|\\n)*)) 非贪婪 来修复正则表达式以匹配您希望它匹配的内容:将其更改为 ((?:.|\\n)*?)。这可能也解决了堆栈爆炸问题,因为它会导致正则表达式在它爆炸堆栈之前更早地匹配。但我不知道它是否能正常工作;我真的对 MS 的实现一无所知。

    在我看来,你应该避免反向引用,即使这意味着你的代码有点复杂。我要做的是首先匹配:

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

    然后创建终止字符串:

     "\n=== END " + match[1].str() + " ==="
    

    然后是find() 终止字符串。这意味着您不能再使用正则表达式库的迭代器,这很不幸,但循环仍然非常简单。

    顺便说一句,我觉得奇怪的是,如果它在一行的 end 处,你只识别开始分隔符,如果它在 start 处则识别结束分隔符em> 一行。我的倾向是要求它们都是 full 行。如果您用我的两步方法替换 regex-with-back-reference,则相对容易实现。这可能被认为是另一种暗示,即带有反向引用的正则表达式并不是真正正确的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-21
      • 1970-01-01
      • 2011-05-06
      • 2013-11-12
      相关资源
      最近更新 更多