如何从可能重复的捕获组中检索捕获的子字符串?

How to retrieve the captured substrings from a capturing group that may repeat?

对不起,我用我糟糕的英语很难表达这个问题。那么,我们直接上一个简单的例子。

假设我们有一个主题字符串 "apple:banana:cherry:durian"。我们想要匹配主题,让 </code>、<code></code> 和 <code> 变成 "apple""banana""cherry""durian",分别。我使用的模式是 ^(\w+)(?::(.*?))*$,而 </code> 将是预期的 <code>"apple"。但是,</code> 将是 <code>"durian" 而不是 "banana"

因为要匹配的主题字符串不需要是4项,比如可以是"one:two:three",而</code>和<code>会是"one""three" 分别。同样,缺少中间项。

在这种情况下使用的正确模式是什么?顺便说一句,我将在C++代码中使用PCRE2,所以没有split,一个Perl内置函数。谢谢。

如果输入包含严格由 : 分隔的感兴趣项目,如 item1:item2:item3,如问题中的尝试所示,那么您可以使用正则表达式模式

[^:]+

匹配不是 : 的连续字符,因此子字符串直到第一个 :。这可能也需要捕获,([^:]+),具体取决于整体方法。如何使用它来获得 all 这样的匹配取决于语言。

在 C++ 中有不同的方法来处理这个问题。使用 std::regex_iterator

#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <iostream>

int main()
{
    std::string str{R"(one:two:three)"};
    std::regex r{R"([^:]+)"};

    std::vector<std::string> result{};

    auto it = std::sregex_iterator(str.begin(), str.end(), r);
    auto end = std::sregex_iterator();
    for(; it != end; ++it) {
        auto match = *it;
        result.push_back(match[0].str());
    }

    std::cout << "Input string: " << str << '\n';
    for(auto i : result)
        std::cout << i << '\n';
}

按预期打印。

也可以使用 std::regex_search,即使它在第一次匹配时 returns -- 通过遍历字符串以在每次匹配后移动搜索开始 [​​=32=]

#include <string>
#include <regex>
#include <iostream>

int main()
{
    std::string str{"one:two:three"};
    std::regex r{"[^:]+"};

    std::smatch res;

    std::string::const_iterator search_beg( str.cbegin() );
    while ( regex_search( search_beg, str.cend(), res, r ) )
    {
        std::cout << res[0] << '\n';  
        search_beg = res.suffix().first;
    }
    std::cout << '\n';
}

(有了这个字符串和正则表达式,我们不需要 raw string literal,所以我在这里删除了它们。)


这个问题最初被标记为 perl (没有 c++),也在文本中明确提到它(仍然存在),并且原始这个答案的版本提到 Perl

/([^:]+)/g

/g“修饰符”用于“全局”,以查找所有匹配项。 // 是模式分隔符。

当此表达式绑定 (=~) 到具有目标字符串的变量时,整个表达式 returns 在预期列表的上下文中使用时的匹配列表,这因此可以直接赋值给一个数组变量。

my @captures = $string =~ /[^:]+/g;

(当按字面意思使用时,不需要捕获 ()

分配给一个数组提供了这个“list context”。如果在“标量上下文”中使用匹配,其中期望单个值,例如在 if 测试的条件中或被分配给标量变量,则返回单个 true/false (通常是 1'',空字符串)。

重复捕获组只会捕获最后一次迭代的值。相反,您可以使用 \G 锚点来获得连续的匹配项。

如果整个字符串只能包含以冒号分隔的单词字符:

(?:^(?=\w+(?::\w+)+$)|\G(?!^):)\K\w+

模式匹配:

  • (?:非捕获组
    • ^ 断言字符串开始
    • (?=\w+(?::\w+)+$) 从当前位置断言 1+ 个单词字符和 1+ 个重复 : 和 1+ 个单词字符直到字符串结尾
    • |
    • \G(?!^): 声明上一场比赛结束时的位置,而不是开始和比赛 :
  • )关闭非捕获组
  • \K\w+忘记目前匹配的内容,匹配1+个单词字符

Regex demo

也只允许从字符串的开头开始的单词,并允许在单词字符之后的其他字符:

\G:?\K\w+

Regex demo