从std::regex中提取原始正则表达式模式

22

我有一个函数,它试图将给定的字符串与给定的正则表达式模式进行匹配。如果不匹配,它应该创建一个字符串指示这种情况,并包括失败的正则表达式模式和字符串内容。类似于这样:

bool validate_content(const std::string & str, const std::regex & pattern, std::vector<std::string> & errors)
{
    if ( false == std::regex_match(str, pattern) )
    {
        std::stringstream error_str;
        // error_str << "Pattern match failure: " << pattern << ", content: " << str;
        errors.push_back(error_str.str());
        return false;
    }
    return true;
}

然而,正如您所看到的,这条被注释的语句提出了一个难题:是否有可能恢复正则表达式对象的原始模式?

显然,有一个变通方法是提供原始模式字符串(而非或同时提供)正则表达式对象,然后使用它。 但是,我当然更希望不需要包含额外的工作来重新创建每次调用此函数时的正则表达式对象(每次调用函数都要重新解析模式的成本很高)或者将正则表达式模式连同正则表达式对象一起传递(容易出现拼写错误和错误,除非我提供一个为我执行此操作的包装器,这不太方便)。

我正在使用 Ubuntu 14.04 上的 GCC 4.9.2。


1
@Jarod42:答案已经在回答中给出了,所以为什么要写成评论呢?-( - Lightness Races in Orbit
2个回答

16
boost::basic_regex 对象有一个 str() 函数,用于返回用于构造正则表达式的字符字符串(它的副本)。 (它们还提供 begin()end() 接口,用于返回到字符序列的迭代器,以及一种内省捕获子表达式的机制。)
这些接口最初在 TR1 正则表达式标准化建议中提出,但在 2003 年被移除,之后采用了 n1499:简化 basic_regex 中的接口,其中引用了以下内容:

basic_regex 不应保留其初始化器的副本

basic_regex 模板有一个成员函数 str,它返回一个字符串对象,该对象持有用于初始化 basic_regex 对象的文本...虽然偶尔查看初始化器字符串可能是有用的,但如果不使用它,我们应该遵循规则,即不用就不付费。正如 fstream 对象不携带打开时使用的文件名一样,basic_regex 对象也不应携带它们的初始化文本。如果有人需要跟踪该文本,可以编写一个包含该文本和 basic_regex 对象的类。


C++不想提供像Java的toString基础设施那样有用的东西,这很遗憾。 - user1633272
1
这个答案表明了C++设计决策仍然非常注重运行时性能。软件开发的性能也是一种性能,而C++在这方面表现相当差。basic_regex不可哈希等问题又该如何解决呢? - Johannes Overmann
OP没有询问关于boost的问题。 - Craig B
1
@CraigB:没错,但这是C++正则表达式库历史的一部分。我觉得从C++标准提案中摘录更有意义,因为它是对某些东西做出反应的上下文。当然,你可能不同意。此外,使用Boost可能比手动编写包装器更好。 - rici

9
根据标准N4431§28.8/2类模板basic_regex [re.regex] (强调我的):

特化类型的basic_regex对象负责将charT对象序列转换为内部表示形式。未指定此表示形式的形式,也未指定操作正则表达式的算法如何访问它。 [注意:实现通常会声明一些函数模板作为basic_regex的友元来实现这一点 — 结束语]

因此,basic_regex对象不需要在内部保留原始字符序列。

因此,在创建regex时必须存储字符序列。例如:

struct RegexPattern {
  std::string pattern;
  std::regex  reg;
};
...
bool validate_content(const std::string & str, const RegexPattern & pattern, std::vector<std::string> & errors) {
    if(false == std::regex_match(str, pattern.reg)) {
        std::stringstream error_str;
        error_str << "Pattern match failure: " << pattern.pattern << ", content: " << str;
        errors.push_back(error_str.str());
        return false;
    }
    return true;
}

另一个更优雅的解决方案是由@Praetorian提出的,但效率略低(我没有对这两个版本进行基准测试,因此不确定)。将模式字符串保留并将其作为输入参数传递给函数validate_content,并在内部创建regex对象,如下所示:
bool validate_content(const std::string & str, const string & pattern, std::vector<std::string> & errors) {
    std::regex reg(pattern);
    if(false == std::regex_match(str, reg)) {
        std::stringstream error_str;
        error_str << "Pattern match failure: " << pattern << ", content: " << str;
        errors.push_back(error_str.str());
        return false;
    }
    return true;
}

3
这是一件非常合理且不应该改变的事情,但它确实非常令人恼火。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit 我完全同意。 - 101010
2
另一个选择是根据regex模式是否需要在其他地方使用,让validate_content接受一个包含该模式的std::string参数,并在本地构建regex - Praetorian
1
@Praetorian 是的,你的解决方案看起来更优雅。我可以将它添加到我的答案中吗? - 101010
2
好的,如果它被频繁地调用,那么我就不会这样做。:) 无论如何,我认为你在这里拥有了所有需要的东西。耶! - Lightness Races in Orbit
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接