我要创建一个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)
,这可能是你在这里能做的最好的,但涉及到很多字符串复制,我认为这不是严格必要的