用于regex的转义特殊字符



我要创建一个std::regex(__FILE__)作为单元测试的一部分,该单元测试检查一些打印文件名的异常输出。

在Windows上,如果输入

则失败。

regex_error(error_escape):表达式包含无效的转义字符,或结尾转义。

因为__FILE__宏展开包含未转义的反斜杠。

是否有一种更优雅的方式来逃避反斜杠,而不是通过结果字符串循环(即使用std算法或一些std::string函数)?

文件路径可以包含许多在正则表达式模式中具有特殊含义的字符。在一般情况下,仅转义反斜杠不足以进行健壮的检查。

即使是简单的路径,如C:Program Files (x86)VendorProductapp.exe,也包含几个特殊字符。如果您想将其转换为正则表达式(或正则表达式的一部分),则不仅需要转义反斜杠,还需要转义圆括号和句号(点)。

幸运的是,我们可以用更多的正则表达式来解决正则表达式问题:

std::string EscapeForRegularExpression(const std::string &s) {
  static const std::regex metacharacters(R"([.^$-+()[]{}|?*)");
  return std::regex_replace(s, metacharacters, "\$&");
}

(文件路径不能包含*?,但我包含了它们以保持函数的通用性。)

如果你不遵守"无原始循环"准则,一个可能更快的实现是避免正则表达式:

std::string EscapeForRegularExpression(const std::string &s) {
  static const char metacharacters[] = R"(.^$-+()[]{}|?*)";
  std::string out;
  out.reserve(s.size());
  for (auto ch : s) {
    if (std::strchr(metacharacters, ch))
      out.push_back('\');
    out.push_back(ch);
  }
  return out;
}

虽然循环增加了一些混乱,但这种方法允许我们在metacharacters的定义上降低转义级别,这是优于regex版本的可读性。

polymapper

接受接受一个元素并返回一个范围的操作,即"map操作"。

生成一个接受容器的函数对象,并对每个元素应用"map操作"。它返回与容器相同的类型,其中每个元素都通过"map操作"展开/收缩。

template<class Op>
auto polymapper( Op&& op ) {
  return [op=std::forward<Op>(op)](auto&& r) {
    using std::begin;
    using R=std::decay_t<decltype(r)>;
    using iterator = decltype( begin(r) );
    using T = typename std::iterator_traits<iterator>::value_type;
    std::vector<T> data;
    for (auto&& e:decltype(r)(r)) {
      for (auto&& out:op(e)) {
        data.push_back(out);
      }
    }
    return R{ data.begin(), data.end() };
  };
}

escape_stuff:

auto escape_stuff = polymapper([](char c)->std::vector<char> {
  if (c != '\') return {c};
  else return {c,c};
});

生活例子。

int main() {
  std::cout << escape_stuff(std::string(__FILE__)) << "n";
}

这种方法的优点是可以排除干扰容器内部的操作。您编写的代码与字符或元素混淆,而整体逻辑不是您的问题。

缺点是polymapper有点奇怪,并且会完成不必要的内存分配。(这些可以优化,但这会使代码更复杂)。

EDIT

最后,我改用了@AdrianMcCarthy更稳健的方法。


下面是我解决这个问题的一个不太优雅的方法,以防有人偶然发现这个问题,实际上是在寻找一个解决方法:

std::string escapeBackslashes(const std::string& s)
{
    std::string out;
    for (auto c : s)
    {
        out += c; 
        if (c == '\') 
            out += c;
    }
    return out;
}

std::regex(escapeBackslashes(__FILE__));

它是O(N),这可能是你在这里能做的最好的,但涉及到很多字符串复制,我认为这不是严格必要的

最新更新