31

a*表示零个或多个实例:一个权利?

那么为什么会$_ = "a"; s/a*/e/g产生:ee

可能的答案:它将字符串:“a”替换为:“e”,并将空字符串:“”替换为:“e”。或者它正在用一个字母:e 替换仅仅没有字母:a 或者它正在用一个:e 替换:a 的“零次出现”

好吧,但是:

$_ = "b"; s/a*/e/g产生:ebe

它似乎正在替换左侧的空字符串:b以及右侧空字符串:b

好的。但是为什么它不这样做: " a" ?为什么它不替换左侧的空字符串:a以及右侧空字符串:a 以及字母:a 本身来获取:eee

出现的次数为零:左侧的出现次数与右侧的出现次数一样多!

4

5 回答 5

27

您对结果为何如此"ee""ebe"完全准确的分析。

“/g”修饰符使正则表达式匹配一次,然后从上次匹配停止的位置再次尝试匹配。

差异的原因(它不会替换 左侧的空字符串"a")是因为它"*"贪婪的 - 它匹配大多数可能的字符。来自perldoc perlre

默认情况下,量化的子模式是“贪婪的”,也就是说,它会尽可能多地匹配(给定一个特定的起始位置),同时仍然允许模式的其余部分匹配。

所以它匹配零个“a”,看看它是否可以匹配更多。由于字符串中有更多的“a”,它会再匹配一个。尝试匹配更多。没有任何?完毕。所以我们匹配第一个“a”。

然后,“/g”使我们再次尝试匹配(从上次匹配完成后停止的位置开始),现在匹配空(零“a”)字符串。

于 2012-08-07T23:46:44.890 回答
19

使用 Damian Conway 的优秀Regexp::Debugger,我尝试了这个:

perl -MRegexp::Debugger -E '$_ = "a"; s/a*/e/g; say'

并得到这个输出,以防它让事情变得更清楚,以事件记录模式显示。通过替换运行的第一遍匹配产生以下事件集:

a               | a*              |   Starting regex match
a               | a*              |     Trying a literal character zero-or-more times (as many as possible)
                | a*              |     Matched
                |                 |   Regex matched in 3 steps

这表明“a”第一次匹配,它被“e”替换。

第一次完成匹配后,调试器让我从同一个程序运行第二个匹配:

                | <~~             |   Back-tracking in regex
                | a*              |   Back-tracked and restarting regex match
                | a*              |     Trying a literal character zero-or-more times (as many as possible)
                | a*              |     Matched
                |                 |   Regex matched in 3 steps

这表明原始“a”(现在的“e”)之后的“”第二次匹配并替换为“e”。

不幸的是,要么我不知道如何读取输出,要么 Regexp::Debugger 在这一点上感到困惑或其他什么,但它再次重复,但没有进行替换。

                | <~~             |   Back-tracking in regex
                | a*              |   Back-tracked and restarting regex match
                | a*              |     Trying a literal character zero-or-more times (as many as possible)
                | a*              |     Matched
                |                 |   Regex matched in 3 steps

无论如何,Perl 已经匹配了第三次并且出于某种原因决定这次不进行替换,或者 Regexp::Debugger 或者我只是感到困惑。

编辑:我通过查看perldoc perlre解决了我的困惑:

“更高级别的循环在迭代之间保留了一个额外的状态:最后一个匹配是否为零长度。为了打破循环,零长度匹配之后的下一个匹配被禁止长度为零。这个禁止与回溯交互(参见“回溯”),因此如果最佳匹配的长度为零,则选择次佳匹配。”

于 2012-08-08T00:06:33.157 回答
8

首先,正如人们所说,a*是贪婪;如果它可以匹配“a”,它将不匹配空字符串。其次,/g匹配将尽可能多地匹配,但不会在同一位置连续两次进行零长度匹配,因为这意味着模式没有进展。如果可以,则强制该模式进行一些其他非零长度匹配,否则将失败。

在“a”上运行s/a*/e/g时,首先a*在位置 0 匹配“a”(并前进到位置 1),因此“a”被替换为“e”。然后a*匹配位置 1 的空字符串(并且不前进),因此将“”替换为“e”。现在我们仍然在位置 1,并且a*被禁止再次匹配空字符串,并且不能再匹配任何内容,因此模式失败并且 perl 尝试前进到字符串中的下一个字符。但是我们已经到了字符串的结尾,所以输出是“ee”。

s/a*/e/g在 "b" 上运行时,首先a*匹配位置 0 处的空字符串(并且不前进),将 "" 替换为 "e"。然后,位置 0 的另一个匹配被禁止,因此模式前进到位置 1(越过未被替换的“b”)。然后a*匹配位置 1 的空字符串,并将其替换为“e”;同样,禁止在同一位置匹配两次,perl 不能超出字符串的末尾,所以结果是“ebe”。

最后,想象一下s/a*/e/g在“ab”上运行。a*在位置 0 匹配 "aa",用 "e" 替换,然后前进到位置 2;a*匹配位置 2 的空字符串,替换为“e”并且不前进;a*无法进行非空匹配并失败;“b”被扫描;a*匹配位置 3 的空字符串,替换为 "e" 并且不前进;字符串的结尾。所以结果是“eebe”,perl 将确认。

于 2012-08-08T00:17:47.023 回答
2

您认为它是不一致的,因为您认为它实际上替换了“位置 0 处的 'a 的序列”时替换了“位置 0 处的空字符串”。当输入ab.

$_ = "a"; s/a*/e/g

  1. 在 pos 0 处尝试:在 pos 0 处匹配 1 个字符。Pos = 1。
  2. 在 pos 1 尝试:在 pos 1 匹配 0 个字符。Pos = 1。
  3. 在 pos 1 尝试:在 pos 1匹配 0 char。糟糕,已经这样做了,所以在那个位置失败。位置 = 2。

$_ = "b"; s/a*/e/g

  1. 在 pos 0 处尝试:在 pos 0 处匹配 0 个字符。Pos = 0。
  2. Try at pos 0: Match 0 char at pos 0.哎呀,已经这样做了,所以在那个位置失败了。位置 = 1。
  3. 在 pos 1 尝试:在 pos 1 匹配 0 个字符。Pos = 1。
  4. 在 pos 1 尝试:在 pos 1匹配 0 char。糟糕,已经这样做了,所以在那个位置失败。位置 = 2。

如果你想在 pos 0 匹配一个空字符串,你必须要求它这样做。

>perl -E"say 'a' =~ s/^|a*/e/gr;"
eee

>perl -E"say 'b' =~ s/^|a*/e/gr;"
ebe
于 2012-08-08T04:44:09.120 回答
1

非常好奇。在 RHEL 5 上使用 Perl 5.12.1,输出确实如图所示:

$ perl -e '$_ = "a"; s/a*/e/g; print "$_\n";'
ee
$

我能想到的最好的猜测(原因)是第a*一个匹配a, 产生第一个e,然后匹配 , 之后的空字符串a作为第二个e。让我们尝试一些变体:

$ perl -e '$_ = "a"; s/^a*/e/g; print "$_\n";'
e
$ perl -e '$_ = "a"; s/a*$/e/g; print "$_\n";'
ee
$ perl -e '$_ = "a"; s/a+/e/g; print "$_\n";' 
e
$

这些变体中的第一个和第三个产生了我所期望的答案。第二个确实让我感到困惑,仍然。

$ perl -e '$_ = "a\n"; s/a*/e/g; print "$_\n";'
ee
e
$

嗯……

于 2012-08-07T23:49:18.160 回答