20

我很难理解\G锚在 PHP 风格的正则表达式中是如何工作的。

我倾向于认为(即使我可能错了),\G而不是^在发生相同字符串的多个匹配的情况下使用它。

有人可以举一个\G应该如何使用的例子,并解释它是如何工作的以及为什么工作吗?

4

2 回答 2

14

更新

\G强制模式只返回作为连续匹配链一部分的匹配。从第一个匹配开始,每个后续匹配都必须在匹配之前。如果你打破链条,比赛就结束了。

<?php
$pattern = '#(match),#';
$subject = "match,match,match,match,not-match,match";

preg_match_all( $pattern, $subject, $matches );

//Will output match 5 times because it skips over not-match
foreach ( $matches[1] as $match ) {
    echo $match . '<br />';
}

echo '<br />';

$pattern = '#(\Gmatch),#';
$subject = "match,match,match,match,not-match,match";

preg_match_all( $pattern, $subject, $matches );

//Will only output match 4 times because at not-match the chain is broken
foreach ( $matches[1] as $match ) {
    echo $match . '<br />';
}
?>

这直接来自文档

反斜杠的第四种用途是用于某些简单的断言。断言指定必须在匹配中的特定点满足的条件,而不消耗主题字符串中的任何字符。下面描述了对更复杂的断言使用子模式。反斜杠断言是

 \G
    first matching position in subject

仅当当前匹配位置位于匹配的起点时,\G 断言才为真,如 preg_match() 的 offset 参数所指定。当 offset 的值非零时,它与 \A 不同。

http://www.php.net/manual/en/regexp.reference.escape.php

您将不得不向下滚动该页面,但它确实存在。

ruby 中有一个非常好的例子,但在 php 中也是一样的。

Anchor \z 和 \G 如何在 Ruby 中工作?

于 2013-02-15T15:53:25.780 回答
9

\G将匹配匹配边界,该边界可以是字符串的开头,也可以是使用最后一个匹配的最后一个字符的点。

当您需要进行复杂的标记化时,它特别有用,同时还要确保标记有效。

示例问题

让我们以标记这个输入为例:

input 'some input in quote' more input   '\'escaped quote\''   lots@_$of_fun    ' \' \\  ' crazy'stuff'

进入这些标记(我~用来表示字符串的结尾):

input~
some input in quote~
more~
input~
'escaped quote'~
lots@_$of_fun~
 ' \  ~
crazy~
stuff~

该字符串由以下各项组成:

  • 单引号字符串,允许转义\and ',并且保留空格。可以使用单引号字符串指定空字符串。
  • OR 不带引号的字符串,由一系列非空白字符组成,并且不包含\or '
  • 2 个未加引号的字符串之间的空格将分隔它们。不需要空格来分隔其他情况。

为了简单起见,让我们假设输入不包含换行(在实际情况下,您需要考虑它)。它会增加正则表达式的复杂性,而不会证明这一点。

单引号字符串'(?:[^\\']|\\[\\'])*+'
的 RAW 正则表达式是 未引用字符串的 RAW 正则表达式是[^\s'\\]++
你不需要太在意上面的 2 段正则表达式。

下面的解决方案\G可以确保当引擎找不到任何匹配时,从字符串开头到最后一个匹配位置的所有字符都已被消耗掉。由于它不能跳过字符,因此引擎将在无法找到两种标记规范的有效匹配时停止匹配,而不是在字符串的其余部分中抓取随机内容。

建造

在构建的第一步,我们可以把这个正则表达式放在一起:

\G(?:'((?:[^\\']|\\[\\'])*+)'|([^\s'\\]++))

或者简单地说(这不是正则表达式 - 只是为了更容易阅读):

\G(Singly_quote_regex|Unquoted_regex)

这将只匹配第一个标记,因为当它第二次尝试匹配时,匹配会在之前的空格处停止'some input...


我们只需要添加一点,以允许 0 或更多空间,以便在随后的匹配中,消耗上次匹配留下的位置的空间:

\G *+(?:'((?:[^\\']|\\[\\'])*+)'|([^\s'\\]++))

上面的正则表达式现在将正确识别标记,如此处所示


可以进一步修改正则表达式,以便在引擎无法检索任何有效令牌时返回字符串的其余部分:

\G *+(?:'((?:[^\\']|\\[\\'])*+)'|([^\s'\\]++)|((?s).+$))

由于按从左到右的顺序尝试交替,((?s).+$)当且仅当前面的字符串不构成有效的单引号或不带引号的标记时,最后一个选项才会匹配。这可用于检查错误。

第一个捕获组将包含单引号字符串中的文本,需要额外处理才能转换为所需的文本(这里并不相关,所以我把它留给读者作为练习)。第二个捕获组将包含未加引号的字符串。第三个捕获组充当输入字符串无效的指示符。

最终正则表达式的演示

结论

上面的示例演示了\Gin 标记化的一种使用场景。可能还有其他我没有遇到过的用法。

于 2013-02-15T16:56:52.657 回答