gpt4 book ai didi

c++ - 正则表达式匹配可选数字

转载 作者:太空狗 更新时间:2023-10-29 21:17:43 26 4
gpt4 key购买 nike

我有一个当前使用 regex 表达式解析的文本文件,它运行良好。文件格式定义明确,2 个数字,由任何空格分隔,后跟可选注释。

现在,我们需要向该文件添加一个额外的(但可选的)第三个数字,使格式为由空格分隔的 2 或 3 个数字以及可选的注释。

我有一个 regex 对象,它至少匹配所有必要的行格式,但我没有任何运气来实际捕获第三个(可选)数字,即使它存在。

代码:

#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]
[]

如您所见,它匹配所有预期的输入格式,但永远无法真正捕获第三个数字,即使它存在也是如此。

我目前正在使用 GCC 5.1.1 对此进行测试,但实际目标编译器将是 GCC 4.8.2,使用 boost::regex 而不是 std::regex.

最佳答案

让我们对以下示例进行逐步处理。

 16653    2      1
^

^ 是当前匹配的偏移量。此时,我们处于模式中:

\s*?(\d+)\s*?(\d+)\s*?(\d+|\s*?)!*?.*?
^

(我已将 [[:space:]] 简化为 \s 并将 [[:digit:]] 简化为 \d 为简洁起见。


\s*? 匹配,然后 (\d+) 匹配。我们最终处于以下状态:

 16653    2      1
^
\s*?(\d+)\s*?(\d+)\s*?(\d+|\s*?)!*?.*?
^

同样的事情:\s*? 匹配,然后 (\d+) 匹配。状态是:

 16653    2      1
^
\s*?(\d+)\s*?(\d+)\s*?(\d+|\s*?)!*?.*?
^

现在,事情变得更棘手了。

这里有一个\s*?,一个惰性量词。引擎尝试匹配任何内容,并查看模式的其余部分是否匹配。所以它尝试交替。

第一个选择是 \d+,但它失败了,因为你在这个位置没有数字。

第二个选择是\s*?,之后没有其他选择。比较懒,先尝试匹配空字符串吧。

下一个标记是 !*?,但它匹配空字符串,然后是 .*?,它将匹配字符串末尾的所有内容(这样做是因为您正在使用 regex_match - 它会用 regex_search 匹配空字符串)。

此时,您已经成功地到达了模式的末尾,并且您得到了一个匹配项,而不是被迫将 \d+ 与字符串进行匹配。

问题是,整个模式部分最终都是可选的:

\s*?(\d+)\s*?(\d+)\s*?(\d+|\s*?)!*?.*?
\__________________/

那么,你能做什么?您可以像这样重写您的模式:

\s*?(\d+)\s+(\d+)(?:\s+(\d+))?\s*(?:!.*)?

Demo (添加 anchor 以模仿 regex_match 行为)

这样,您就迫使正则表达式引擎考虑 \d 而不是在空字符串上进行延迟匹配。不需要惰性量词,因为 \s\d 是不相交的。

!*?.*? 也是次优的,因为 !*? 已被以下 .*? 覆盖。我将其重写为 (?:!.*)?require 在评论的开头使用 !,如果不存在则匹配会失败。

关于c++ - 正则表达式匹配可选数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31896055/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com