std::regex - 前瞻断言并不总是有效

PCB*_*PCB 5 c++ regex regex-lookarounds

我正在编写一个模块,该模块将一些字符串替换为文本以提供给脚本语言。该语言的语法模糊不清,因此表达式以括号和空格分隔的符号为界,其中大多数以 '$' 开头。像这样的正则表达式似乎应该在适当的符号边界处给出匹配:

auto re_match_abc = std::regex{ "(?=.*[[:space:]()])\\$abc(?=[()[:space:]].*)" };
Run Code Online (Sandbox Code Playgroud)

但是在我的环境中(Visual C++ 2017, 15.9.19,targeting C++-17)它可以匹配前面没有合适边界的字符串:

std::cout << "  $abc   -> " << std::regex_replace(" $abc ", re_match_abc, "***") << std::endl;
std::cout << " ($abc)  -> " << std::regex_replace("($abc)", re_match_abc, "***") << std::endl;
std::cout << "xyz$abc  -> " << std::regex_replace("xyz$abc ", re_match_abc, "***") << std::endl;
std::cout << " $abcdef -> " << std::regex_replace(" $abcdef", re_match_abc, "***") << std::endl;

// Result from VC++ 2017:
//
//       $abc   ->  ***
//      ($abc)  -> (***)
//     xyz$abc  -> xyz***     <= What's going wrong here?
//      $abcdef ->  $abcdef
Run Code Online (Sandbox Code Playgroud)

为什么那个正则表达式忽略了在匹配文本之前至少有一个空格或括号的正向预测要求?

[我意识到还有其他方法可以完成这项工作,并且要真正稳健地完成,也许我应该使用某些东西将字符串转换为令牌流,但是对于我的直接工作(并且因为创作字符串的人被处理坐在我旁边,所以我们可以协调)我认为现在可以使用正则表达式替换。]

Kev*_*vin 2

您需要使用积极的后视。你真正想要的是这样的:

auto re_match_abc = std::regex{ "(?<=[[:space:]()])\\$abc(?=[()[:space:]])" };
Run Code Online (Sandbox Code Playgroud)

您可以在https://regex101.com/等网站上尝试一下(只需删除 C++ 字符串所需的转义反斜杠)。它解释了正则表达式的每个部分正在做什么,并向您显示所有匹配的内容。

请记住,这也会匹配诸如)$abc)

编辑:std::regex显然不支持lookbehind。对于您的具体情况,您可以尝试这样的操作:

    auto re_match_abc = std::regex{ "([[:space:]()])\\$abc(?=[()[:space:]])" };
    std::cout << "  $abc   -> " << std::regex_replace(" $abc ", re_match_abc, "$1***") << std::endl;
    std::cout << " ($abc)  -> " << std::regex_replace("($abc)", re_match_abc, "$1***") << std::endl;
    std::cout << "xyz$abc  -> " << std::regex_replace("xyz$abc ", re_match_abc, "$1***") << std::endl;
    std::cout << " $abcdef -> " << std::regex_replace(" $abcdef", re_match_abc, "$1***") << std::endl;
Run Code Online (Sandbox Code Playgroud)

输出:

  $abc   ->  *** 
 ($abc)  -> (***)
xyz$abc  -> xyz$abc 
 $abcdef ->  $abcdef
Run Code Online (Sandbox Code Playgroud)

在这里试试

这里我们有一个普通的捕获组,而不是向后查找。在替换中,我们发出捕获的任何内容(括号或空格),后跟我们想要替换的实际字符串$abc