【问题标题】:C++ regex: Get index of the Capture Group the SubMatch matched toC++ 正则表达式:获取与 SubMatch 匹配的捕获组的索引
【发布时间】:2018-06-22 09:40:34
【问题描述】:

上下文。我正在开发一个 Lexer/Tokenizing 引擎,它将使用正则表达式作为后端。词法分析器接受规则,这些规则定义了标记类型/ID,例如

<identifier> = "\\b\\w+\\b".

正如我所设想的,要进行基于正则表达式匹配的标记化,正则表达式定义的所有规则都包含在捕获组中,并且所有组都由 OR 分隔。

在执行匹配时,我们生成的每个匹配必须有一个与之匹配的捕获组的索引。我们使用这些 ID 将匹配项映射到令牌类型。

那么这个问题的问题就出现了——如何获取组的ID

Similar question 在这里,但它没有为我的具体问题提供解决方案。

正是我的问题here,但它在 JS 中,我需要 C/C++ 解决方案。

假设我有一个正则表达式,由 OR 分隔的捕获组组成:

(\\b[a-zA-Z]+\\b)|(\\b\\d+\\b)

匹配整数或字母词。

我的问题要求正则表达式子匹配匹配的捕获组的索引是已知的,例如匹配字符串时

foo bar 123

将完成 3 次迭代。每次迭代匹配的组索引为0 0 1,因为前两个匹配匹配第一个捕获组,最后一个匹配匹配第二个捕获组。

我知道在标准的 std::regex 库中这并不完全可能(regex_token_iterator 不是解决方案,因为我不需要跳过任何匹配项)。

我对@9​​87654329@ 或 PCRE 正则表达式库了解不多。

完成这项任务的最佳方法是什么?使用哪个库和方法?

【问题讨论】:

  • 遍历所有匹配项,直到找到一个非空匹配项,即匹配项。
  • @Barmar 不是。 std::regex 结果将只是一个非空子匹配数组。例如。当第 6 组是唯一匹配的组时,std::match_results 结果数组将包含 2 个条目:整个正则表达式匹配,以及第 6 组的子匹配,它位于数组的索引 1 处,因为它是第一个匹配。我们无法从中获取正则表达式中组的索引6
  • 你确定吗?在所有其他语言中,组是根据 RE 编号的,而不是它们是否匹配。您需要能够引用特定匹配项,而不必担心之前的组是否匹配。这似乎使捕获组无法在 C++ 中可靠地使用。
  • 或者,单独运行这些正则表达式。
  • which would be at index 1 of the array - 这绝不是真的!匹配对象在匹配之前根据定义的捕获组的数量预分配。当您通过索引 match[group number] 取消引用 match_object 时访问的实际 sub_match 对象返回指向存在于其他数组中的 sub_match 对象的指针。如果在取消引用 match_object 时它不包含指针,则它是 NULL。因此,您所做的是迭代组数,取消引用 match_object。如果它不是NULL,那就是匹配的那个。确保未设置 no-subs

标签: c++ regex tokenize lexer capturing-group


【解决方案1】:

您可以使用sregex_iterator 获取所有匹配项,一旦有匹配项,您可以分析std::match_results 结构并仅获取参与匹配的组的ID-1 值(注意只有一个组这里将匹配第一个或第二个),可以使用m[index].matched 方便地检查:

std::regex r(R"((\b[[:alpha:]]+\b)|(\b\d+\b))");
std::string s = "foo bar 123";
for(std::sregex_iterator i = std::sregex_iterator(s.begin(), s.end(), r);
                         i != std::sregex_iterator();
                         ++i)
{
    std::smatch m = *i;
    std::cout << "Match value: " << m.str() << " at Position " << m.position() << '\n';

    for(auto index = 1; index < m.size(); ++index ){
        if (m[index].matched) {
            std::cout << "Capture group ID: " << index-1 << std::endl;
            break;
        }
    }
}

请参阅C++ demo。输出:

Match value: foo at Position 0
Capture group ID: 0
Match value: bar at Position 4
Capture group ID: 0
Match value: 123 at Position 8
Capture group ID: 1

请注意,R"(...)" 是一个原始字符串文字,无需在其中使用双反斜杠。

另外,indexfor 循环的开头设置为1,因为第 0 组是整个匹配项,但您希望组 ID 从零开始,这就是为什么 1 是稍后减去。

【讨论】:

  • 它按预期工作。原来我的问题不在于系统,而在于我使用的正则表达式!在我的原始测试代码(我没有在这里发布)中,我使用了这个正则表达式:(\b\w+\b)|(\b\d+\b),它只匹配第一组,因为\w 也匹配数字!而这个简单的错误导致我怀疑系统并发布问题!
  • 我认为使用m[index].matched 而不是!m[index].str().empty() 可能会更好。 matched 字段 "indicates if this match was successful"
  • @LVK 很好的提示,这在一般情况下要好得多,匹配的子组实际上可以保存一个空字符串。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-30
  • 2013-02-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-28
  • 1970-01-01
相关资源
最近更新 更多