【问题标题】:std::regex escape backslashes in file path文件路径中的 std::regex 转义反斜杠
【发布时间】:2016-08-30 13:30:50
【问题描述】:

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

在 Windows 上失败:

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

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

有没有比循环遍历结果字符串更优雅的方法来转义反斜杠(即使用std 算法或某些std::string 函数)?

【问题讨论】:

  • __FILE__ 应该只打印文件名。你需要完整的路径吗?
  • @Hayt "__FILE__ 应该只打印文件名。"不一定
  • 是的,如果他不需要它们,他可以在这里查看:msdn.microsoft.com/en-us/library/027c4t2s.aspx 假设问题不是缺少引号,您已经回答了。并假设他使用 MSVC 编译器
  • @NicolasHolthaus 也许std::transform() 加上一个 lambda 函数可能有助于以优雅的方式编写它。
  • 也许最好是编写自己的函数,然后逐个字符地遍历字符串并复制它,当它找到一个 \ 时再添加一个。

标签: c++ regex string c++11


【解决方案1】:

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

即使是简单的路径,例如C:\Program Files (x86)\Vendor\Product\app.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 的定义上减少一定程度的转义,这比正则表达式版本更具可读性。

【讨论】:

【解决方案2】:

这里是polymapper

它接受一个接受元素并返回一个范围的操作,即“映射操作”。

它生成一个带有容器的函数对象,并将“映射操作”应用于每个元素。它返回与容器相同的类型,其中每个元素都已通过“映射操作”进行扩展/收缩。

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};
});

live example.

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

这种方法的优点是可以排除弄乱容器内脏的行为。您编写的代码与字符或元素混淆,整体逻辑不是您的问题。

缺点是polymapper有点奇怪,做了不必要的内存分配。 (这些可以优化出来,但这会使代码更加复杂)。

【讨论】:

    【解决方案3】:

    编辑

    最后,我切换到了@AdrianMcCarthy 的more robust approach


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

    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) 可能与您在此处所做的一样好,但涉及大量字符串复制,我认为这不是绝对必要的。

    【讨论】:

    • 所有这些只是转义反斜杠,这不足以将 Windows 文件路径转换为有效的正则表达式模式。它不会对路径名中的其他正则表达式元字符做任何事情,比如括号。
    • @AdrianMcCarthy 当然,但这就是它的全部目的。它用于单元测试,而不是作为通用 regex 制造商,并解决了我需要它解决的唯一一个问题。
    猜你喜欢
    • 2013-07-11
    • 2017-03-16
    • 2013-12-05
    • 2014-02-04
    • 2013-07-23
    • 2023-04-08
    • 2017-06-24
    • 2016-11-20
    • 1970-01-01
    相关资源
    最近更新 更多