如何从可能重复的捕获组中检索捕获的子字符串?
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+个单词字符
也只允许从字符串的开头开始的单词,并允许在单词字符之后的其他字符:
\G:?\K\w+
对不起,我用我糟糕的英语很难表达这个问题。那么,我们直接上一个简单的例子。
假设我们有一个主题字符串 "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+个单词字符
也只允许从字符串的开头开始的单词,并允许在单词字符之后的其他字符:
\G:?\K\w+