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