在 C++ 中使用“DEFINED”子表达式组合复杂的正则表达式

Posted

技术标签:

【中文标题】在 C++ 中使用“DEFINED”子表达式组合复杂的正则表达式【英文标题】:Composing complex regular expressions with "DEFINED" subexpressions in C++ 【发布时间】:2021-03-04 02:27:17 【问题描述】:

我正在尝试用 C++ 编写一个正则表达式来匹配一个 base64 编码的字符串。我对在 Perl 中编写复杂的正则表达式非常熟悉,所以我从这个开始:

use strict;
use warnings;

my $base64_regex = qr
(?(DEFINE)
    (?<B64>[A-Za-z0-9+/])
    (?<B16>[AEIMQUYcgkosw048])
    (?<B04>[AQgw])
)
^(
    ((?&B64)4)*
    (
        (?&B64)4|
        (?&B64)2(?&B16)=|
        (?&B64)(?&B04)=2
    )
)?$x;

# "Hello World!" base64 encoded 
my $base64 = "SGVsbG8gV29ybGQh";

if ($base64 =~ $base64_regex)

    print "Match!\n";

else

    print "No match!\n"

输出:

Match!

然后我尝试在 C++ 中实现一个类似的正则表达式:

#include <iostream>
#include <regex>

int main()

    std::regex base64_regex(
        "(?(DEFINE)"
            "(?<B64>[A-Za-z0-9+/])"
            "(?<B16>[AEIMQUYcgkosw048])"
            "(?<B04>[AQgw])"
        ")"
        "^("
            "((?&B64)4)*"
            "("
                "(?&B64)4|"
                "(?&B64)2(?&B16)=|"
                "(?&B64)(?&B04)=2"
            ")"
        ")?$");

    // "Hello World!" base64 encoded 
    std::string base64 = "SGVsbG8gV29ybGQh";

    if (std::regex_match(base64, base64_regex))
    
        std::cout << "Match!" << std::endl;
    
    else
    
        std::cout << "No Match!" << std::endl;
    

但是当我运行代码时,我得到一个异常,告诉我它不是一个有效的正则表达式。

捕获异常并打印“what”字符串也无济于事。它给我的只是以下内容:

regex_error(error_syntax)

显然,我可以用我的预定义子模式摆脱“DEFINE”块,但这会使整个表达式很难阅读......而且,嗯......我希望能够保持我自己的几年后我回到它时的代码大声笑,所以这不是一个好的选择。

如何让类似的正则表达式在 C++ 中工作?

注意:这一切都必须在单个“std::regex”对象中完成,因为我正在编写一个库,用户可以在其中传递一个字符串来定义他们自己的正则表达式,我想要如果需要,这些用户能够在他们的正则表达式中“定义”类似的子表达式。

【问题讨论】:

看看this post 你也可以试试 (?P 等是否有效 至少,您可以使用插值/连接构建模式(因为它们都不是递归的) C++ 不支持递归正则表达式,但 Boost 可以提供帮助。例如,请参阅 this post 和 this post,这是 boost page @tjwrona1992 太好了 :) 感谢您告诉我,很高兴知道它运行良好。感谢您提供我写的内容,但我建议您发布您所做的(并“接受”它)——在这里有一个答案会很好。 【参考方案1】:

字符串连接怎么样?

#define B64 "[A-Za-z0-9+/]"
#define B16 "[AEIMQUYcgkosw048]"
#define B04 "[AQgw]"

std::regex base64_regex(
        "^("
            "(" B64 "4)*"
            "("
                B64 "4|"
                B64 "2" B16 "=|"
                B64 B04 "=2"
            ")"
        ")?$");

【讨论】:

看起来确实很干净,但不幸的是它不适用于我的用例。最终,我正在编写一个库,它允许用户从 XML 文件中提供他们自己的正则表达式字符串,并且我希望用户能够自己“定义”子表达式。因此,这一切都必须在一个字符串中完成。【参考方案2】:

我接受了 cmets 的建议并检查了“boost”正则表达式,因为它支持“Perl”正则表达式。我试了一下,效果很好!

#include <boost/regex.hpp>

boost::regex base64_regex(
    "(?(DEFINE)"
        "(?<B64>[A-Za-z0-9+/])"
        "(?<B16>[AEIMQUYcgkosw048])"
        "(?<B04>[AQgw])"
    ")"
    "("
        "((?&B64)4)*"
        "("
            "(?&B64)4|"
            "(?&B64)2(?&B16)=|"
            "(?&B64)(?&B04)=2"
        ")"
    ")?", boost::regex::perl);

【讨论】:

以上是关于在 C++ 中使用“DEFINED”子表达式组合复杂的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们需要在 C++ 头文件中使用“#if defined Identifier”?

在 C++ 中使用 std::complex<T> 创建复无穷大

C++解决C++ “multiple definition of .. first defined here“问题

使用组合框过滤子表单

C++ 正则表达式提取子字符串

在 C++ 中使用 std::regex 匹配精确的子字符串