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_match
、std::regex_search
或std::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 正则表达式 ^(?<!(.|\n)])
模拟 Perl/Python/PCRE \A
,它在字符串的开头匹配但不在换行符之后,它翻译成英语为“匹配没有前一个字符”。
您可以使用 (?!(.|\n))$
模拟 Perl/Python/PCRE \z
,它仅在字符串末尾匹配。要获得 \Z
的效果,它只匹配字符串结尾,但允许在字符串结尾之前有一个换行符,只需添加一个可选换行符:\n?(?!(.|\n))$
。
【讨论】:
以上是关于std::regex,匹配字符串的开始/结束的主要内容,如果未能解决你的问题,请参考以下文章