为啥在 regex char 可选时找不到匹配项
Posted
技术标签:
【中文标题】为啥在 regex char 可选时找不到匹配项【英文标题】:Why are no matches found when ever regex char optional为什么在 regex char 可选时找不到匹配项 【发布时间】:2016-02-19 08:59:04 【问题描述】:我对为什么会发生这种情况感到非常困惑,并希望有人能解释正则表达式引擎中发生的事情的机制。
在进行代码战练习时,“您的意思是……?”在 javascript 中,我试图计算 word1 中有多少字符出现在 word2 中。我正在尝试将每个字符作为其自己的匹配组进行匹配,以便稍后我可以计算数组中成功匹配的数量,并使用它来找出这两个单词的相似程度。
请不要给我如何解决代码战挑战的提示,只是帮助我了解这里发生了什么。
我试过了:
'berry'.match(/(c?)(h?)(e?)(r?)(r?)(y?)/)
没有找到匹配项
> ["", "", "", "", "", "", ""]
这对我来说是个谜。搜索了 Regular-expressions.info 后,?
(使前面的字符可选)是贪婪的,所以虽然没有匹配对正则表达式有效,但不应该首先被贪婪的版本打败吗?我期待这个:
> ["", "", "", "e", "r", "r", "y"]
我尝试过的其他事情: - 樱桃匹配樱桃按我的预期工作
'cherry'.match(/(c?)(h?)(e?)(r?)(r?)(y?)/)
> ["cherry", "c", "h", "e", "r", "r", "y"]
cherl match cherry 也符合我的预期
'cherl'.match(/(c?)(h?)(e?)(r?)(r?)(y?)/)
> ["cher", "c", "h", "e", "r", "", ""]
如果我从最终的y
中删除?
,它也会按预期工作:
'berry'.match(/(c?)(h?)(e?)(r?)(r?)(y)/)
> ["erry", "", "", "e", "r", "r", "y"]
那么为什么在最后的y
上添加?
意味着我不再看到任何匹配的字符?
虽然我在 JS 中尝试过,但我在 PY 和 PCRE 中得到了相同的结果
【问题讨论】:
?
表示一个或无,并且总是为真 - 找不到任何内容被视为匹配项。它是贪婪的,因为它试图首先找到一些东西(而不是相反的惰性量词:尝试满足于nothing)。
您没有在 JS 中使用 /g
,因此您得到了一个匹配项:字符串开头的空位置。看看this demo
感谢 Jan,Wiktor - 特别是 Wiktor - 这很有意义。因此,正则表达式引擎在尝试匹配的字符串上从左到右工作,如果没有 g
标志,它会在字符串的开头找到零字符的匹配。
【参考方案1】:
那么为什么在最后的 y 上添加
?
意味着我不再看到任何匹配的字符?
您一开始使用的模式 - /(c?)(h?)(e?)(r?)(r?)(y?)/
- 可以匹配空字符串,因为所有子模式都是可选的(即可以匹配零次出现)。当您只寻找一个匹配项时(例如,在 JavaScript 中使用 String#match
),您将始终获得位于字符串开头的匹配项(因为在大多数情况下,正则表达式引擎会从左到右分析字符串),或者是一个空字符串(如果第一个字符不能与第一个子模式匹配),或者如果前导或所有子模式匹配,则为某个子字符串。
所以,berry
以 b
开头。 /(c?)(h?)(e?)(r?)(r?)(y?)/
以可选的c
开头,因此,b
无法与c
匹配,因此失败。 b
不能与 h
、e
、r
、另一个 r
、y
匹配。注意如果你把最后一个y?
改成b?
,你will get a b
in the match。
如果您不在 JS 正则表达式中使用 /g
(全局)标志,引擎将只检查一个匹配项。它会在开头找到它 - 一个空字符串,将其返回并收工。 If you use /g
,它会检查所有位置,第二个匹配会得到你想要的结果。但是,当您将String#match()
与基于/g
的正则表达式一起使用时,您将丢失捕获的子字符串。使用RegExp#exec()
可以访问这些子匹配项。
请注意,/(c?)(h?)(e?)(r?)(r?)(y)/
会为您提供匹配项,因为最后一个 y
是必填,并且该模式不能再匹配空字符串。因此,当引擎在berry
中看到b
时,匹配失败,继续检查e
之前的下一个位置,并在那里找到匹配项。因此,在这种情况下不需要/g
。
【讨论】:
以上是关于为啥在 regex char 可选时找不到匹配项的主要内容,如果未能解决你的问题,请参考以下文章
Google Play Developer API - 为啥在用户执行重新订阅时找不到linkedPurchaseToken?