提问人:Henley Wing Chiu 提问时间:11/9/2023 更新时间:11/10/2023 访问量:95
为什么我对正则表达式的扫描没有发现超过 1 个匹配项?[复制]
How come my scan of a regex is not picking up more than 1 match? [duplicate]
问:
我有一个正则表达式,它应该提取单词“year”之前 7 个“单词”或更少的任何数字。当我在字符串上运行它时,它不知何故只返回 1 匹配项,而不是 2。为什么它没有返回第 2 场比赛?正则表达式本身确实与句子的第二部分匹配,但是使用扫描时,它似乎没有返回它
str = "Just 5 years of experience and 12 years of experience ok"
regex = /(\d+)(?:\s+\S+){0,7}\s*year/i
str.scan(regex) # returns just [["5"]]
regex.match("5 years of experience")
=> #<MatchData "5 years" 1:"5">
regex.match("12 years of experience") #regex does match the 2nd part but scan misses it
=> #<MatchData "12 years" 1:"12">
答:
扫描提供不同的捕获和非捕获行为
使用捕获组时,String#scan 仅返回与捕获组匹配的匹配项。目前,不在捕获组中,并且显式未捕获,因此不会返回。文档明确指出:\s*year
(?:\s+\S+){0,7}
如果模式包含组,则每个结果都是一个数组,每个组包含一个条目。
您还通过限制数字后面的内容和使用字符来过度限制捕获组的上下文,因此第二组数字永远不会匹配。请参阅积极的展望,了解可以在不使用字符的情况下指定上下文的替代方案。
假设您不想在捕获中包含该单词,这将起作用:years
string = "Just 5 years of experience and 12 years of experience ok"
regexp = /
(?ix) # set case-insensitive and extended modes
(\d+) # capture one or more digits when
\s+ # ...followed by at least one whitespace character
years? # ...followed by "year" or "years"
\b # ...followed by a word boundary
/
string.scan regexp
# => [["5"], ["12"]]
对于更紧凑的表达式,请将内联模式移到主正则表达式之外,并将它们追加到分隔正则表达式文本的字符中。具体而言,您通常不需要将扩展模式用于生产用途(它主要用于记录表达式),因此您可以像这样收紧正则表达式,同时返回与上面所示的扩展模式相同的结果:(?ix)
/(\d+)\s+years?\b/i
将扫描结果转换为整数
虽然不是原始问题的一部分,但您很可能希望扫描的最终结果为数字而不是字符串。如果是这种情况,只需修改扫描以 #flatten 结果,然后将 Array 成员强制为 Integers。例如:
string = "Just 5 years of experience and 12 years of experience ok"
regexp = /(\d+)\s+years?\b/i
string.scan(regexp).flatten.map &:to_i
# => [5, 12]
虽然这超出了您最初问题的范围,但这是合乎逻辑的下一步。我在这里介绍它,希望它能为您省去处理嵌套的 String 数组对象的麻烦,您将从使用 Sring#scan 和捕获中获得。
只需在范围内使用非贪婪运算符{0,7}?
(\d+)(?:\s+\S+){0,7}?\s*years?
https://regex101.com/r/gHX25u/1
概述
( \d+ ) # (1)
(?: \s+ \S+ ){0,7}?
\s* years?
评论
{0,7}?
{0,7}?
不是不贪婪。 是一个消耗原子,意思是“零或一”。它遵循一个范围运算符,表示“前一组的0到7”,这一事实只会使原子更加混乱,但这并不能改变两者本质上都不是贪婪的事实,尽管覆盖意外非空白的范围可能会过度消耗。?
?
?
{0,7}
?
??
{0,7}
是一个范围运算符,它是一种量词。 是零或一范围,但两者都引用它们正在量化的原子。与其争论贪婪的语义,不如说三重网络是贪婪最好在捕获组或非捕获组中固定,或者重构匹配,而不是限制匹配范围的数量。如果人们想要的只是“修复我的代码”,那么 CoPilot 或 ChatGPT 同样能够提供答案。我将把它留在那里,以避免对定义或语义的进一步争论。{0,7}?
评论
/(\d+)(?:\s+\S+){0,7}?\s*year/i
/(\d+)\s+years?/i