不同的待遇' \在regex_replace()中替换g ++和boost中的字符串

时间:2016-06-15 08:21:16

标签: c++ c++11 gcc boost

将gcc从版本4.8.5升级到版本5.3.1后,我想我 可以摆脱boost的正则表达式实现(升级版本1.54.0)和 使用gcc提供的那个(在版本4.9之前它没有使用gcc 据我所知)。然而,事实证明这是一个问题,因为那两个 实现的行为不同:

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

int main() {
    std::string s="\\needs_another_backslash";
    std::string reg("^(\\\\)(needs)(.+)");
    std::string rep("\\\\got$3");
    std::regex sr(reg);
    boost::regex br(reg);
    std::cout<<"string before replacement:\n"<<s<<std::endl<<
        "regular expression:\n"<<reg<<std::endl<<
        "replacement string:\n"<<rep<<std::endl<<
        "std::regex_replace:\n"<<std::regex_replace(s,sr,rep)<<std::endl<<
        "boost::regex_replace:\n"<<boost::regex_replace(s,br,rep)<<std::endl;
    return 0;
}

这给出了以下输出:

string before replacement: \needs_another_backslash regular expression: ^(\\)(needs)(.+) replacement string: \\got$3 std::regex_replace: \\got_another_backslash boost::regex_replace: \got_another_backslash

好像提升对待&#39; \&#39;特别是在替换字符串中 而gcc没有。由于std :: regex_replace的替换字符串中的反向引用的神奇字符是&#39; $&#39; (它也是作为示例证明的提升),我倾向于认为gcc是对的。然而,在许多其他程序(例如vim)中,它是&#39; \。因此,提升可能有助于治疗&#39; \&#39;特别。那么谁是对的?

1 个答案:

答案 0 :(得分:0)

首先,std示例实际上不是gcc的问题,而是C ++标准的问题,gcc(在本例中)是兼容的。标准在28.5.2中说明:

  

当正则表达式匹配由新字符串替换时,新的   string应使用ECMAScript replace使用的规则构造   ECMA-262中的函数,第15.5.4.11节String.prototype.replace。此外,   在搜索和替换操作期间所有非重叠的出现   应定位和替换正则表达式,并输入部分   与表达式不匹配的表达式应不加改变地复制到输出中   字符串。

并且ECMA声明:

  

否则,让newstring表示将replaceValue转换为String的结果。结果是从原始输入String派生的String值,方法是将每个匹配的子字符串替换为从newstring派生的字符串,方法是用表22中指定的替换文本替换newstring中的字符。这些$替换是从左到右完成的,并且,一旦进行了这样的替换,新的替换文本不再需要进一步替换。例如,“$ 1,$ 2”.replace(/(\ $(\ d))/ g,“$$ 1- $ 1 $ 2”)返回“$ 1- $ 11,$ 1- $ 22”。与以下任何表格都不匹配的新闻字符串中的$保留原样。

(如果part:replaceValue是一个函数。)

没有提到有关替换转义序列的内容。用firefox试过:

var test = "\\needs_another_backslash";
test = test.replace(/^(\\)(needs)(.+)/, "\\\\got$3");
alert(test);

结果:\\got_another_backslash

提升documentation州:

  

效果:如果fmt是以null结尾的字符串或char_type的容器,则将字符序列[fmt.begin(),fmt.end())复制到OutputIterator out。对于fmt中的每个格式说明符或转义序列,将该序列替换为它所代表的字符,或者它所引用的* this中的字符序列。 flags中指定的位掩码确定识别哪些格式说明符或转义序列,默认情况下,这是ECMA-262,ECMAScript语言规范,第15章第5.4.11节String.prototype.replace使用的格式。

此外,它还声明了match_type_flags

  

指定在用新字符串替换正则表达式匹配时,使用ECMA-262中ECMAScript替换函数使用的规则构建新字符串,ECMAScript语言规范,第15章第5.4.11节字符串.prototype.replace。 (FWD.1)。

     

这在功能上与Perl格式字符串规则相同。

     

[...]

在linux上尝试使用perl 5.18.2:

my $test = "\\needs_another_backslash";
$test =~ s/^(\\)(needs)(.+)/\\\\got$3/;
print "$test\n";

导致\\got_another_backslash

使用std::string reg("^(\\\\)(needs)(.+)");时,传递字符串文字时,reg会保存字符串^(\\)(needs)(.+)(不是字面值,因此省略引号!),而std::string rep("\\\\got$3");代表字符{hold} { {1}}。

但是在解释上显然存在差异。假设我们有std和boost两个和相同的ECMAScript引擎。

然后,std和boost一致的做法是将\\got$3字符串编译为正则表达式:

reg

我认为通过创建std / boost :: regex类的实例可以很好地反映这一点。

然后出现差异:std将sprintf(b, "/%s/", reg); sr /* br, respectively */ = ECMAScriptEngine::compileFromSource(b); ssr传递给ECMAScript引擎,以便直接调用rep(当然没有这样的现实中的s函数 - 只是让我们假设我们可以这样做。)

boost也可以编译rep字符串(旁注:我没有安装boost,所以我自己也没有验证这个行为......):

s.(String.prototype.replace)(sr, rep);

然后拨打引擎sprintf(b, "'%s'", rep); // note: '', not //! ecma_rep = ECMAScriptEngine::compileFromSource(b);

有趣的是,boost不会编译源字符串s,它再次与std ...

一致

最后,我认为,标准实施反映了我们实际想做的事情:

s.(String.prototype.replace)(sr, ecma_rep);

VS

s.replace(regex, string);
s.replace(/reg/, rep);
(std::string).replace(std::regex(std::string), std::string);
std::regex_replace(s, std::regex(reg), rep);

不确定这是否足以说一个是对的而另一个是错的,但是(这意味着错误的一个是错误的!)。可能我们甚至必须保留第三种选择:两种方法都是有效的(所以两种方法都是正确的,没有一种是错误的),不幸的是,它们是不相容的......