\G在正则表达式中有什么实际应用?

23
我不太清楚\G操作符的使用/需要。
我在perldoc中读到:

您可以使用\G锚定在上一次匹配结束的字符串上开始下一次匹配。

我真的不理解这个说法。当我们使用\g时,通常会移动到上一个匹配后的字符。
正如示例所示:
$_ = "1122a44";  
my @pairs = m/(\d\d)/g;   # qw( 11 22 44 )  

然后它说:
如果你使用 \G 锚点,你将强制匹配从 22 开始,并以 a 结尾。
$_ = "1122a44";
my @pairs = m/\G(\d\d)/g;

正则表达式无法匹配那里,因为它找不到数字,所以下一个匹配失败,匹配运算符返回它已经找到的一对内容。我也不理解这句话:"如果您使用\G锚定,您会强制在22之后进行匹配,并以a开头"。但是没有\G,匹配也会在a处尝试吗?那么这个句子的含义是什么呢?我看到在示例中只打印11和22这两个数字对。所以44没有尝试。示例还显示,使用c选项将在while之后索引44。老实说,通过所有这些,我无法理解该运算符的用处和何时应用它。可以有意义的例子帮助我理解吗?更新:我认为我没有理解这个关键句子:如果您使用\G锚定,您会强制在22之后进行匹配,并以a .正则表达式无法在此处进行匹配,因为它没有找到数字,因此下一个匹配失败并且匹配运算符返回它已经找到的数字对。这似乎意味着当匹配失败时,正则表达式不会进一步尝试,并且与答案中的示例一致。另外:在字母a处匹配失败后,perl将重置pos(),同一字符串的下一个匹配从开头开始。

此答案已添加到Stack Overflow正则表达式FAQ,位于“锚点”下。 - aliteralmind
3个回答

24

\G是一个锚点;它指示了匹配强制开始的位置。当\G存在时,它不能在字符串的任意后面一点开始匹配;当\G不存在时,它可以。

在将字符串解析为离散部分时,它非常有用,您不希望跳过其他内容。例如:

my $string = " a 1 # ";
while () {
    if ( $string =~ /\G\s+/gc ) {
        print "whitespace\n";
    }
    elsif ( $string =~ /\G[0-9]+/gc ) {
        print "integer\n";
    }
    elsif ( $string =~ /\G\w+/gc ) {
        print "word\n";
    }
    else {
        print "done\n";
        last;
    }
}

\G输出:

whitespace
word
whitespace
integer
whitespace
done

没有:

whitespace
whitespace
whitespace
whitespace
done
请注意,我正在演示使用标量上下文 /g 匹配,但是 \G 同样适用于列表上下文 /g 匹配,并且上面的代码可以轻松修改以使用它:
my $string = " a 1 # ";
my @matches = $string =~ /\G(?:(\s+)|([0-9]+)|(\w+))/g;
while ( my ($whitespace, $integer, $word) = splice @matches, 0, 3 ) {
    if ( defined $whitespace ) {
        print "whitespace\n";
    }
    elsif ( defined $integer ) {
        print "integer\n";
    }
    elsif ( defined $word ) {
        print "word\n";
    }
}

我在CLI中运行它,它的行为与您所说的相同,但是我不理解这一点。没有\G 这个标记时,$string =~ /\s+/gc匹配空格,由于我们有一个匹配项,正则表达式应该移动到a。但似乎并没有这样做,而是继续打印“whitespace”,这意味着它“卡住”了第一个if语句。但是为什么? - Jim
2
@mpapec:位置绑定到字符串上,并且可以使用http://perldoc.perl.org/functions/pos.html进行更改/设置。 - ysth
1
你似乎试图将人们说/展示的内容塞进你对其工作方式的想法中;这使你无法倾听人们实际在说什么:\G告诉它必须开始匹配的位置。 - ysth
@ysth:啊!所以它会匹配第一个空格。然后在下一次迭代中,它将尝试将空格与 a 匹配,但会失败,因此它将继续处理下一个字符,依此类推。因此我们打印了三次空格。对吗?所以添加 \G 后,空格被匹配并打印,然后尝试匹配第一个 if 中的 a,它失败了,然后正则表达式不会继续进行,而是尝试其他 else 的剩余部分? - Jim
1
停止考虑“第一次失败”,开始思考约束;必须在特定位置匹配。就像(?=x)表示必须在x之前匹配,\A表示必须在字符串的开头匹配。 - ysth
显示剩余13条评论

15

但是没有\G,匹配还是会在a处尝试对吧?

没有\G,匹配不会被限制在那里开始。它会尝试,但如果需要的话,它会尝试从稍后开始。您可以将每个模式视为在前面隐含了\G.*?

添加\G,就能明确其含义。

$_ = "1122a44";  
my @pairs = m/\G     (\d\d)/xg;   # qw( 11 22 ) 
my @pairs = m/\G .*? (\d\d)/xg;   # qw( 11 22 44 )
my @pairs = m/       (\d\d)/xg;   # qw( 11 22 44 )

说实话,从这些中我无法理解这个运算符的用处以及何时应该使用它。

如您所见,添加\G会得到不同的结果,因此其用处在于获取您想要的结果。


@Jim:在 perldoc 的例子中,\G 强制匹配应该是连续/不间断的。一旦它停止匹配,就不会再尝试匹配了。 - Zaid
@Zaid:匹配应该是连续的/不间断的。我不明白这个。没有 \G, 它仍然以相同的方式匹配,例如 11-22-a4(失败)-44 - Jim
@Jim:看一下示例代码中的注释;顶部匹配两次,底部匹配三次(跳过失败的a)。 - ysth
1
“没有\G仍然可以匹配相同的方式”,真的吗?不,它不能。我展示了它所匹配的内容:使用\G可以匹配11和22,使用\G可以匹配11、22和44。 - ikegami
3
@TLP,是的,它就像“^”一样,只不过它匹配的位置是上一个匹配结束的地方,而不是字符串的开头。 - ikegami
显示剩余7条评论

6

有趣的答案,很多都是有效的,但我猜仍然没有完全解释清楚。

\G会强制下一次匹配发生在上一次匹配结束的位置。

基本上:

$str="1122a44";
while($str=~m/\G(\d\d)/g) {
#code
}

第一个匹配 = "11" 第二个匹配被强制从22开始,并且是\d\d,所以结果是 "22" 第三个 'try' 被强制从 "a" 开始,但那不是 \d\d,所以失败了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接