mkl*_*nt0 6 .net ruby python regex node.js
许多正则表达式引擎在单行字符串中匹配.* 两次,例如,在执行基于正则表达式的字符串替换时:
在许多引擎中有第二个匹配,即空字符串 ; 也就是说,即使第一个匹配消耗了整个输入字符串,.*也会再次匹配,然后匹配输入字符串末尾的空字符串.
^.*我的问题是:
这种行为有充分的理由吗?一旦输入字符串被完全消耗,我就不会期望再次尝试找到匹配项.
除了试验和错误,你能从文档/正则表达式方言/标准中收集哪些引擎表现出这种行为?
更新:雷沃乐于助人的回答解释了如何与当前的行为的; 至于潜在的原因,请参阅此相关问题.
表现出行为的语言/平台:
# .NET, via PowerShell (behavior also applies to the -replace operator)
PS> [regex]::Replace('a', '.*', '[$&]'
[a][] # !! Note the *2* matches, first the whole string, then the empty string
# Node.js
$ node -pe "'a'.replace(/.*/g, '[$&]')"
[a][]
# Ruby
$ ruby -e "puts 'a'.gsub(/.*/, '[\\0]')"
[a][]
# Python 3.7+ only
$ python -c "import re; print(re.sub('.*', '[\g<0>]', 'a'))"
[a][]
# Perl 5
$ echo a | perl -ple 's/.*/[$&]/g'
[a][]
# Perl 6
$ echo 'a' | perl6 -pe 's:g/.*/[$/]/'
[a][]
# Others?
Run Code Online (Sandbox Code Playgroud)
不表现出这种行为的语言/平台:
# Python 2.x and Python 3.x <= 3.6
$ python -c "import re; print(re.sub('.*', '[\g<0>]', 'a'))"
[a] # !! Only 1 match found.
# Others?
Run Code Online (Sandbox Code Playgroud)
bobble bubble带来了一些很好的相关点:
如果你让懒惰像
.*?,你甚至可以在一些3场比赛,并在其他2场比赛.与...相同.??.一旦我们使用了一个开始锚点,我认为我们应该只获得一个匹配,但有趣的是它似乎在PCRE中^.*?提供了两个匹配a,而^.*应该导致一个匹配到处.
这是一个PowerShell代码片段,用于测试跨语言的行为,具有多个正则表达式:
注意:假设Python 3.x可用,python3而Perl 6 可用perl6.
您可以将整个代码段直接粘贴到命令行上,并从历史记录中调用它以修改输入.
& {
param($inputStr, $regexes)
# Define the commands as script blocks.
# IMPORTANT: Make sure that $inputStr and $regex are referenced *inside "..."*
# Always use "..." as the outer quoting, to work around PS quirks.
$cmds = { [regex]::Replace("$inputStr", "$regex", '[$&]') },
{ node -pe "'$inputStr'.replace(/$regex/g, '[$&]')" },
{ ruby -e "puts '$inputStr'.gsub(/$regex/, '[\\0]')" },
{ python -c "import re; print(re.sub('$regex', '[\g<0>]', '$inputStr'))" },
{ python3 -c "import re; print(re.sub('$regex', '[\g<0>]', '$inputStr'))" },
{ "$inputStr" | perl -ple "s/$regex/[$&]/g" },
{ "$inputStr" | perl6 -pe "s:g/$regex/[$/]/" }
$regexes | foreach {
$regex = $_
Write-Verbose -vb "----------- '$regex'"
$cmds | foreach {
$cmd = $_.ToString().Trim()
Write-Verbose -vb ('{0,-10}: {1}' -f (($cmd -split '\|')[-1].Trim() -split '[ :]')[0],
$cmd -replace '\$inputStr\b', $inputStr -replace '\$regex\b', $regex)
& $_ $regex
}
}
} -inputStr 'a' -regexes '.*', '^.*', '.*$', '^.*$', '.*?'
Run Code Online (Sandbox Code Playgroud)
正则表达式的示例输出^.*,它确认了bobble bubble期望使用start anchor(^)在所有语言中只产生一个匹配:
VERBOSE: ----------- '^.*'
VERBOSE: [regex] : [regex]::Replace("a", "^.*", '[$&]')
[a]
VERBOSE: node : node -pe "'a'.replace(/^.*/g, '[$&]')"
[a]
VERBOSE: ruby : ruby -e "puts 'a'.gsub(/^.*/, '[\\0]')"
[a]
VERBOSE: python : python -c "import re; print(re.sub('^.*', '[\g<0>]', 'a'))"
[a]
VERBOSE: python3 : python3 -c "import re; print(re.sub('^.*', '[\g<0>]', 'a'))"
[a]
VERBOSE: perl : "a" | perl -ple "s/^.*/[$&]/g"
[a]
VERBOSE: perl6 : "a" | perl6 -pe "s:g/^.*/[$/]/"
[a]
Run Code Online (Sandbox Code Playgroud)
有点有趣的问题。我不会先询问您的问题,而是征求您的评论。
一旦输入字符串被完全消耗,为什么要将没有任何剩余的事实视为空字符串呢?
留下一个称为主题字符串末尾的位置。这是一个位置,可以匹配。与其他零宽度断言和断言锚点\b, \B, ^, $... 一样,点星号.*可以匹配空字符串。这高度依赖于正则表达式引擎。例如,TRegEx 的做法有所不同。
如果这样做,这不会导致无限循环吗?
不,这是正则表达式引擎要处理的主要工作。它们会提出一个标志并存储当前游标数据以避免发生此类循环。Perl 文档是这样解释的:
对这种能力的常见滥用源于使用正则表达式进行无限循环的能力,其中一些无害的内容如下:
Run Code Online (Sandbox Code Playgroud)'foo' =~ m{ ( o? )* }x;
o?开头的匹配项,foo由于字符串中的位置不会因匹配项而移动,因此o?会由于*量词而一次又一次匹配。创建类似循环的另一种常见方法是使用循环修饰符/g......因此,Perl 通过强制打破无限循环来允许这样的构造。对于贪婪量词给出的较低级别的循环和修饰符或运算符
*+{}等较高级别的循环, 其规则是不同的。/gsplit()当 Perl 检测到重复表达式与零长度子字符串匹配时,较低级别的循环将被中断(即循环被破坏)。
现在回到你的问题:
这种行为有充分的理由吗?
就在这里。每个正则表达式引擎都必须应对大量的挑战才能处理文本。其中之一是处理零长度匹配。你的问题引出了另一个问题,
问:匹配零长度字符串后引擎应该如何处理?
答:这要看情况。
它匹配它然后提出一个标志,以便不再与(相同)匹配相同的位置?图案。在 PCRE 中,.*匹配整个主题字符串,然后在其后立即停止。在最后,当前位置是 PCRE 中有意义的位置,位置可以匹配或断言,因此还有一个位置(零长度字符串)需要匹配。PCRE 再次遍历正则表达式(如果g启用了修饰符)并在主题末尾找到匹配项。
然后 PCRE 尝试前进到下一个直接位置以再次运行整个过程,但由于没有剩余位置而失败。
你看,如果你想阻止第二场比赛的发生,你需要以某种方式告诉引擎:
^.*
Run Code Online (Sandbox Code Playgroud)
或者为了更好地了解正在发生的事情:
(?!$).*
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
231 次 |
| 最近记录: |