std::regex,匹配字符串的开始/结束

Posted

技术标签:

【中文标题】std::regex,匹配字符串的开始/结束【英文标题】:std::regex, to match begin/end of string 【发布时间】:2016-09-22 17:46:57 【问题描述】:

在 JS 正则表达式中,符号 ^$ 指定 字符串的开始和结束。只有使用/m 修饰符(多行模式),它们才匹配行首和行尾 - CR/LF 之前和之后的位置。

但在std::regex/ECMAscript 模式下,符号^$ 始终匹配行首和行尾

在 std::regex 中有什么方法可以定义 字符串的开始和结束 匹配点吗?换句话说:支持 javascript 多行模式...

【问题讨论】:

重点是^$匹配字符串的开头和结尾。见ideone.com/amatBf 和ideone.com/0D7eS7 @WiktorStribiżew 好的,如何修改 ^$ 的样本以匹配行的开始/结束? 我已经提到:行尾是(?=\n|$),行首只能是(^|\n)这样的消费模式。这很不舒服,我知道。如果您确实需要 ^ / $ 的多行行为,则切换到 Boost 正则表达式可能是最佳选择。 【参考方案1】:

默认情况下,ECMAscript 模式已经将^ 视为输入开头 行首,$ 视为输入结尾和 行尾。没有办法让它们匹配 only 开头或结尾的输入,但可以让它们匹配 only 开头或结尾:

当调用std::regex_matchstd::regex_searchstd::regex_replace 时,有一个std::regex_constants::match_flag_type 类型的参数默认为std::regex_constants::match_default

要指定^ 仅匹配行首,请指定std::regex_constants::match_not_bol 要指定$ 仅匹配行尾,请指定std::regex_constants::match_not_eol 由于这些值是位标志,要同时指定两者,只需按位或将它们一起指定 (std::regex_constants::match_not_bol | std::regex_constants::match_not_eol) 请注意,通过指定std::regex_constants::match_continuous,可以在不使用^ 和不管std::regex_constants::match_not_bol 是否存在的情况下隐含输入开始

这在cppreference.com 上的ECMAScript grammar documentation 中得到了很好的解释,我强烈推荐它而不是 cplusplus.com。

警告:我用 MSVC、Clang + libc++ 和 Clang + libstdc++ 进行了测试,目前只有 MSVC 的行为正确。

【讨论】:

来自您的链接 cppreference.com 断言 ^(行首)匹配 1) 紧跟 LineTerminator 字符的位置。 (如果支持,请参阅 LWG 问题 2343) 2) 输入的开头(除非启用了 std::regex_constants::match_not_bol(C++ only))这与所需的完全不同。我需要^ 来匹配“输入的开头”而不是别的。 @c-smile :完全正确,我严重误读了它。答案已更新。 我的心理解析器无法解析:“要指定 $ 仅匹配行尾,请指定 std::regex_constants::match_not_eol”至于我@ 987654344@ 的意思完全相反:如果设置了该标志,则它不应匹配 EOL,只匹配输入结束,对吗?这真的很有意义。在你解释它的方式上,那个标志是无用的。 @c-smile :意思是“不要将first 视为BOL 或last 视为EOL”,不是你想要的。我链接到文档是有原因的。 ;-] 不清楚这里的“第一”和“最后”是什么意思。无论如何,问题是:^ 使用哪些标志来仅匹配输入开始(与$ 和输入结束相同)?在 boost 中,有明确的 \A\z 标记与输入的头/尾显式匹配:boost.org/doc/libs/1_31_0/libs/regex/doc/syntax.html 似乎 std 失去了这个功能。【参考方案2】:

TL;DR

MSVC:^$ 已经匹配 行的开始和结束 C++17:usestd::regex_constants::multiline 选项 其他编译器仅将字符串开头与^ 匹配,将字符串结尾与$ 匹配,无法重新定义其行为。

在除 MSVC 和 C++17 之前的所有 std::regex 实现中,^$ 匹配 字符串 的开头和结尾,而不是一行。请参阅this demo,它在"1\n2\n3" 中找不到任何匹配^\d+$ 正则表达式。当您添加交替时(见下文),there are 3 matches。

但是,在 MSVC 和 C++17 中,^$ 可能匹配 的开始/结束。

C++17

使用std::regex_constants::multiline 选项。

MSVC 编译器

在 Visual Studio 的 C++ 项目中,如下

std::regex r("^\\d+$");
std::string st("1\n2\n3");
for (std::sregex_iterator i = std::sregex_iterator(st.begin(), st.end(), r);
    i != std::sregex_iterator();
    ++i)

    std::smatch m = *i;
    std::cout << "Match value: " << m.str() << " at Position " << m.position() << '\n';

会输出

Match value: 1 at Position 0
Match value: 2 at Position 2
Match value: 3 at Position 4

适用于 C++ 编译器的解决方法

std::regex 中没有 universal 选项可以使锚点在所有编译器中匹配行的开始/结束。你需要用交替来模拟它:

^ -> (^|\n)
$ -> (?=\n|$)

请注意,$ 可以用(?=\n|$) 完全“模拟”(您可以在其中添加更多行终止符符号或符号序列,如(?=\r?\n|\r|$)),但使用^,您无法找到 100% 的解决方法.

由于没有后向支持,您可能需要调整正则表达式模式的其他部分,因为 (^|\n) 比使用后向支持时更频繁地使用捕获组。

【讨论】:

我认为我的措辞有点忙,但我的意思是你最初的假设是错误的。 ^ 只匹配字符串的开头,$ 只匹配字符串的结尾。 “断言 ^(行首)匹配紧跟在 LineTerminator 字符之后的位置......”en.cppreference.com/w/cpp/regex/ecmascript @c-smile:我知道你的意思,但我的回答是基于实践经验。【参考方案3】:

以下代码 sn-p 匹配以 [a-z] 开头的电子邮件地址,后跟 0 或 1 个点,然后是 0 个或多个 a-z 字母,最后以“@gmail.com”结尾。我测试过了。

string reg = "^[a-z]+\\.*[a-z]*@gmail\\.com$";

regex reg1(reg, regex_constants::icase);
reg1(regex_str, regex_constants::icase);
string email;
cin>>email;
if (regex_search(email, reg1))

【讨论】:

【参考方案4】:

您可以使用 Javascript 正则表达式 ^(?&lt;!(.|\n)]) 模拟 Perl/Python/PCRE \A,它在字符串的开头匹配但不在换行符之后,它翻译成英语为“匹配没有前一个字符”。

您可以使用 (?!(.|\n))$ 模拟 Perl/Python/PCRE \z,它仅在字符串末尾匹配。要获得 \Z 的效果,它只匹配字符串结尾,但允许在字符串结尾之前有一个换行符,只需添加一个可选换行符:\n?(?!(.|\n))$

【讨论】:

以上是关于std::regex,匹配字符串的开始/结束的主要内容,如果未能解决你的问题,请参考以下文章

C ++正则表达式替换第一个匹配项

std::regex_match 与字符 é è à

如何用正则表达式匹配指定字符开始和指定字符结束?

在进行 url 编码时,std::regex_replace 对于字符“+”不能正常工作

使用正则表达式匹配字符串的相同开始和结束字符

正则表达式(待补充)