GCC和MSVC之间std :: regex_replace行为的差异

时间:2017-10-25 08:10:04

标签: c++ regex boost

我正在尝试实现一个方法来转义字符串,以便与正则表达式匹配一起使用。

不幸的是,我发现编译器之间存在不一致。使用GCC 7.1和Visual Studio 2015U3编译时,此代码(添加了用于比较的boost实现)会产生不同的结果:

select
    doctor0_.doctorId as doctorId1_3_0_,
    timetables1_.timeTableId as timeTabl1_5_1_,
    doctor0_.address as address2_3_0_,
    doctor0_.branch as branch3_3_0_,
    doctor0_.contactNo as contactN4_3_0_,
    doctor0_.designation as designat5_3_0_,
    doctor0_.email as email6_3_0_,
    doctor0_.fullName as fullName7_3_0_,
    doctor0_.password as password8_3_0_,
    doctor0_.regNo as regNo9_3_0_,
    doctor0_.speciality as special10_3_0_,
    doctor0_.workingTime as working11_3_0_,
    timetables1_.date as date2_5_1_,
    timetables1_.doctorId as doctorId5_5_1_,
    timetables1_.hospital as hospital3_5_1_,
    timetables1_.time as time4_5_1_ 
from
    Doctor doctor0_ 
inner join
    TimeTable timetables1_ 
        on doctor0_.doctorId=timetables1_.doctorId 
where
    doctor0_.fullName='Subash Nisam' 
    and timetables1_.date='2017.03.02'    

GCC

#include <iostream>
#include <regex>
#include <string>
#include <boost/regex.hpp>

std::string regexEscape(const std::string& s)
{
    return std::regex_replace(s, std::regex{ R"([\^\.\$\|\{\}\(\)\[\]\*\+\?\/\\])" }, std::string{ R"(\\\1&)" }, std::regex_constants::match_default | std::regex_constants::format_sed);
}

std::string boostRegexEscape(const std::string& s)
{
    return boost::regex_replace(s, boost::regex{ R"([\^\.\$\|\{\}\(\)\[\]\*\+\?\/\\])" }, std::string{ R"(\\\1&)" }, boost::match_default | boost::format_sed);
}

int main()
{
    std::string test{ R"(123.456^789$123\456|789*123+456(789)123?456)" };
    std::cout << regexEscape(test) << '\n';
    std::cout << boostRegexEscape(test) << '\n';
}

MSVC:

123\\.456\\^789\\$123\\\456\\|789\\*123\\+456\\(789\\)123\\?456
123\.456\^789\$123\\456\|789\*123\+456\(789\)123\?456

这是预期的行为吗?

1 个答案:

答案 0 :(得分:1)

您要求将正则表达式引擎替换为R"(\\\1&)"\\\1&替换模式将其视为sed replacement pattern。在sed中,&代表整场比赛。由于模式中没有ID为1的组,\1引用空字符串。当使用std::regex_replace进行解析时,前两个反斜杠是原始字符串文字中的2个字面反斜杠。

使用Boost时,前两个反斜杠被解析为单个反斜杠,必须转义Boost替换模式中的文字反斜杠才能使用单个文字反斜杠作为替换:

  

Sed样式格式字符串将所有字符视为文字除外:

     

&&符号在输出流中被与正则表达式匹配的全部内容替换。使用\&amp;输出文字&#39;&amp;&#39;字符。

     

\ 指定转义序列。

关于替换模式的其余部分,它将起作用。

您可以使用

 std::regex_replace(s, std::regex{ R"(([.^$|{}()[\]*+?/\\]))" }, std::string{ R"(\$1)" }, std::regex_constants::match_default);

使用Boost,可以使用等效的方法/选项来实现结果的一致性。在这里,使用默认引擎。

关于MSVC和GCC的差异,关于这一点的文档很少。很明显,在两个提到的编译器之间定义文字反斜杠行为是不同的。请注意,许多正则表达式库将文字反斜杠视为正则表达式转义(与Boost相同,请参见上面的参考),并且要定义文字替换反斜杠,您需要在替换模式中加倍文字反斜杠。您在GCC中使用的引擎是ECMAScript

似乎应该如何定义反向间隙替换模式留给每个正则表达式替换实现。当您将其与GCC一起使用时,单个文字\(= "\\")将被视为单个文字替换反斜杠。 MSVC编译器决定使用大多数正则表达式引擎 - 这是有道理的,因为在使用\1时可以使用替换后向引用\9 - std::regex_constants::format_sed - 需要使用文字替换反斜杠转义并使用单个\替换,您需要使用两个文字反斜杠,"\\\\"(或R"(\\)")。