提问人:Todd A. Jacobs 提问时间:3/25/2023 更新时间:3/26/2023 访问量:100
为什么 #match 返回正确的结果,而 #scan 只找到一个字母?
Why does #match return the correct result, but #scan finds only only one letter?
问:
问题背景
我正在使用 Ruby 3.2.1,并且我知道对 Ruby 的正则表达式引擎进行了更改。这可能与此相关,也可能无关。但是,在使用反向引用时,我得到了与 String#match 和 String#scan 出乎意料的不同行为,我不明白为什么。请参阅下面的示例代码。
我的例子,有评论和期望
匹配的工作结果
# Using #match finds the longest string of
# repeated letters, and the letter that is
# repeated.
"gjhfgfcttttdfs".match /(\p{alpha})\1{2,}/
=> #<MatchData "tttt" 1:"t">
扫描时无法正常工作,出现意外结果
# Here, #scan returns only a single sub-
# array with a single letter, which is
# the correct letter. However, I was
# expecting an array-of-arrays with all
# repeated letters.
"gjhfgfcttttdfs".scan /(\p{alpha})\1{2,}/
=> [["t"]]
澄清问题
假设键盘和椅子之间存在问题,为什么 String#scan 没有返回更多匹配项,甚至没有返回一个更长的匹配项?我假设这是我在捕获表达式中的错误,但我无法真正弄清楚我在这里做错了什么。
答:
2赞
Alex
3/25/2023
#1
如果模式包含组,则每个结果都是一个包含 每组条目。https://rubyapi.org/3.2/o/string#method-i-scan
似乎有点模糊,但我认为已经很久了。
>> "aaabccc".match /(\p{alpha})\1{2,}/
=> #<MatchData "aaa" 1:"a">
>> "aaabccc".scan /(\p{alpha})\1{2,}/
=> [["a"], ["c"]]
要获取整个匹配项,您可以捕获它,成为结果的一部分:scan
>> "aaabccc".match /((\p{alpha})\2{2,})/
=> #<MatchData "aaa" 1:"aaa" 2:"a">
# ^ ^
# two captures will return two captured results for each match
>> "aaabccc".scan /((\p{alpha})\2{2,})/
=> [["aaa", "a"], ["ccc", "c"]]
# that's the best i could come up with this
>> "aaabccc".scan(/((\p{alpha})\2{2,})/).map(&:first)
=> ["aaa", "ccc"]
完全匹配仅在块中可用:scan
>> a = []; "aaabccc".scan(/(\p{alpha})\1{2,}/) { a << $& }; a
=> ["aaa", "ccc"]
评论
0赞
Todd A. Jacobs
3/25/2023
你肯定在做某事!我认为需要添加第二个捕获组:,但是是否可以使用单个捕获组执行此操作?尝试使其中一个组不捕获是行不通的,因为您需要对单个字母的反向引用,然后创建工作来剥离子数组中的第二个结果。我想这就是 #match 无论如何都在幕后做的事情,使其成为更好的选择。这让我有点困扰,如果不在 #scan 上进行额外的链接,我无法提取重复的字符。"gjhfgfcttttdfs".scan /((\p{alpha})\2{2,})/ #=> [["tttt", "t"]]
0赞
mu is too short
3/25/2023
您可以混合命名捕获和未命名捕获以获得与 after () 相反的结果,但反过来混合捕获类型会产生 ,一旦引入命名捕获,引用就仅适用于命名捕获。"aaabccc".scan(/((?<c>\p{alpha})\k<c>{2,})/)
SyntaxError
2赞
Stefan
3/25/2023
#2
这两种方法的返回值不同:返回/生成 MatchData
对象,而返回/生成与捕获组内容(如果存在)相对应的字符串。的行为可能在某种程度上不方便,但您可以解决它。match
scan
scan
如果你想要整个对象(工作方式),但对于每个匹配项,你可以调用一个块并使用 $~
(或 )来检索它:MatchData
match
scan
Regexp.last_match
"aaabccc".scan(/(\p{alpha})\1{2,}/) { p $~ }
# #<MatchData "aaa" 1:"a">
# #<MatchData "ccc" 1:"c">
要获取对象数组,您可以利用 enum_for
获取“每个匹配项”枚举器,然后通过以下方式将它们发送到各自的枚举器:MatchData
map
MatchData
$~
"aaabccc".enum_for(:scan, /(\p{alpha})\1{2,}/).map { $~ }
#=> [#<MatchData "aaa" 1:"a">, #<MatchData "ccc" 1:"c">]
评论
String#scan
scan
't'
't'
'gjhfgfffcttttdfs'
scan
scan
gsub
scan
"gjhfgfcttttdfs".gsub(/(\p{alpha})\1{2,}/).to_a #= ["tttt"]
gsub
gsub