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

qyyhg6bp  于 2023-05-30  发布在  其他
关注(0)|答案(2)|浏览(162)

很抱歉,我发现用我糟糕的英语很难表达这个问题。让我们直接来看一个简单的例子。
假设我们有一个主题字符串"apple:banana:cherry:durian"。我们希望匹配主题,并使$1$2$3$4分别变为"apple""banana""cherry""durian"。我使用的模式是^(\w+)(?::(.*?))*$$1将是"apple"。但是,$2将是"durian"而不是"banana"
因为要匹配的主题字符串不需要是4项,例如,它可以是"one:two:three",而$1$2将分别是"one""three"。再次,中间项缺失。
在这种情况下,正确的模式是什么?顺便说一下,我将在C++代码中使用PCRE2,因此没有split,这是一个Perl内置函数。谢谢。

wgmfuz8q

wgmfuz8q1#

如果输入包含严格意义上由:分隔的项目,如item1:item2:item3,则可以使用正则表达式模式

[^:]+

它匹配不是:的连续字符,因此是直到第一个:的子字符串。这可能还需要捕获([^:]+),这取决于整体方法。如何使用它来获得 * 所有 * 这样的匹配取决于语言。
在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,即使它在第一个匹配时返回--通过迭代字符串,在每个匹配之后移动搜索开始

#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';
}

(With这个字符串和正则表达式我们不需要raw string literal,所以我在这里删除了它们。
†这个问题最初被标记为perl(没有c++),也在文本中提到了它;这个答案的原始版本提到Perl时

/([^:]+)/g

//是模式分隔符。/g“修饰符”用于“global”,以查找所有匹配项。
当这个表达式被绑定(=~)到一个带有目标字符串的变量,或者绑定到一个字符串文字,或者绑定到一个产生标量的表达式时,当在一个需要列表的上下文中使用时,整个表达式返回一个匹配列表。因此,它可以直接赋值给数组变量,其中list assignment本身提供上下文

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

(when这是字面上使用的,如图所示,然后捕获()是不需要的)
赋值给一个数组提供了这个“列表上下文”。如果匹配是在“标量上下文”中使用的,在这种情况下需要一个值,比如在if测试的条件中或者被赋值给一个标量变量,那么返回一个true/false(通常是1'',空字符串)。

r55awzrz

r55awzrz2#

重复捕获组将仅捕获最后一次迭代的值。相反,您可以使用\G锚来获得连续的匹配。
如果整个字符串只能包含以冒号分隔的单词字符:

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

模式匹配:

  • (?:非捕获组
  • ^Assert字符串的开始
  • (?=\w+(?::\w+)+$)从当前位置Assert1+单词字符和1+重复:和1+单词字符,直到字符串的结尾
  • |
  • \G(?!^):在上一个匹配的末尾而不是开始处声明位置并匹配:
  • )关闭非捕获组
  • \K\w+忽略目前匹配的内容,匹配1个以上单词字符

Regex demo
要从字符串的开头开始只允许单词,并允许单词字符后的其他字符:

\G:?\K\w+

Regex demo

相关问题