C++ 正则表达式匹配可选数字
我有一个文本文件,当前使用C++ 正则表达式匹配可选数字,c++,regex,C++,Regex,我有一个文本文件,当前使用regex表达式解析,它运行良好。文件格式定义良好,2个数字,由任何空格分隔,后跟可选注释 现在,我们需要在这个文件中添加一个额外的(但可选的)第3个数字,使格式为2或3个数字,用空格分隔,并带有可选注释 我有一个regex对象,它至少匹配所有必要的行格式,但是我没有任何运气能够真正捕获第三个(可选)数字,即使它存在 代码: 如您所见,它匹配所有预期的输入格式,但永远无法实际捕获第三个数字,即使它存在 我目前正在用GCC 5.1.1测试这一点,但实际的目标编译器将是GC
regex
表达式解析,它运行良好。文件格式定义良好,2个数字,由任何空格分隔,后跟可选注释
现在,我们需要在这个文件中添加一个额外的(但可选的)第3个数字,使格式为2或3个数字,用空格分隔,并带有可选注释
我有一个regex
对象,它至少匹配所有必要的行格式,但是我没有任何运气能够真正捕获第三个(可选)数字,即使它存在
代码:
如您所见,它匹配所有预期的输入格式,但永远无法实际捕获第三个数字,即使它存在
我目前正在用GCC 5.1.1测试这一点,但实际的目标编译器将是GCC 4.8.2,使用
boost::regex
而不是std::regex
让我们对以下示例进行逐步处理
16653 2 1
^
^
是当前匹配的偏移量。在这一点上,我们在这里的模式:
\s*?(\d+)\s*?(\d+)\s*?(\d+)\s*?(\d+)!*?*?
^
(我已将brievty的[[:space:][]
简化为\s
,[[:digit:][]code>简化为\d
\s*?
匹配,然后(\d+)
匹配。我们最终处于以下状态:
16653 2 1
^
\s*?(\d+)\s*?(\d+)\s*?(\d+)\s*?(\d+)!*?*?
^
同样的事情:\s*?
匹配,然后(\d+)
匹配。状态为:
16653 2 1
^
\s*?(\d+)\s*?(\d+)\s*?(\d+)\s*?(\d+)!*?*?
^
现在,事情变得更棘手了
这里有一个\s*?
,一个惰性量词。引擎尝试不匹配任何内容,并查看模式的其余部分是否匹配。因此,它尝试替换
第一种选择是\d+
,但它失败了,因为这个位置没有数字
第二个选择是\s*?
,之后没有其他选择。它是惰性的,所以让我们先尝试匹配空字符串
下一个标记是!*?
,但它也与空字符串匹配,然后紧跟着*?
,它将匹配字符串末尾的所有内容(这样做是因为您使用的是正则表达式匹配
-它会将空字符串与正则表达式搜索
匹配)
此时,您已经成功地到达了模式的末尾,并且获得了一个匹配项,而无需强制将\d+
与字符串匹配
问题是,模式的整个部分最终都是可选的:
\s*?(\d+)\s*?(\d+)\s*?(\d+)\s*?(\d+)!*?*?
\__________________/
那么,你能做什么?你可以像这样重写你的模式:
\s*?(\d+)\s+(\d+)(:\s+(\d+)))\s*(?:!*)?
(添加锚以模拟regex\u匹配
行为)
这样,您强制ReGEX引擎考虑<代码> \d>代码>,不能在空字符串上进行懒惰匹配。不必使用惰性量词,因为<代码> < <代码> > <代码> \d>代码>是不相交的。< /p>
!*?*?*?
也是次优的,因为!*?
已经包含在下面的*?
中。我将其改写为(?:!!*)
以要求在注释开始时使用!
,如果没有,匹配将失败。如果是\s*(\d+)\s+(\d+)(\d+)(\d+)(\s+)?:::)\s*)\s*(?:)?
。非贪婪量词让我紧张。@melpomene是的,谢谢,我提交答案太快了,没有考虑原始模式的所有怪癖。
#include <iostream>
#include <regex>
#include <vector>
#include <string>
#include <cassert>
using namespace std;
bool regex_check(const std::string& in)
{
std::regex check{
"[[:space:]]*?" // eat leading spaces
"([[:digit:]]+)" // capture 1st number
"[[:space:]]*?" // each second set of spaces
"([[:digit:]]+)" // capture 2nd number
"[[:space:]]*?" // eat more spaces
"([[:digit:]]+|[[:space:]]*?)" // optionally, capture 3rd number
"!*?" // Anything after '!' is a comment
".*?" // eat rest of line
};
std::smatch match;
bool result = std::regex_match(in, match, check);
for(auto m : match)
{
std::cout << " [" << m << "]\n";
}
return result;
}
int main()
{
std::vector<std::string> to_check{
" 12 3",
" 1 2 ",
" 12 3 !comment",
" 1 2 !comment ",
"\t1\t1",
"\t 1\t 1\t !comment \t",
" 16653 2 1",
" 16654 2 1 ",
" 16654 2 1 ! comment",
"\t16654\t\t2\t 1\t ! comment\t\t",
};
for(auto s : to_check)
{
assert(regex_check(s));
}
return 0;
}
[ 12 3]
[12]
[3]
[]
[ 1 2 ]
[1]
[2]
[]
[ 12 3 !comment]
[12]
[3]
[]
[ 1 2 !comment ]
[1]
[2]
[]
[ 1 1]
[1]
[1]
[]
[ 1 1 !comment ]
[1]
[1]
[]
[ 16653 2 1]
[16653]
[2]
[]
[ 16654 2 1 ]
[16654]
[2]
[]
[ 16654 2 1 ! comment]
[16654]
[2]
[]
[ 16654 2 1 ! comment ]
[16654]
[2]
[]