如何在Crystal语言中将.match的结果作为字符串值访问

3
在许多其他编程语言中,有一个函数,它以正则表达式作为参数,并返回字符串值的数组。这适用于Javascript和Ruby。然而,在Crystal中,.match似乎不接受全局标志,并且它不返回数组,而是返回类型为Regex :: MatchData的结构体。(https://crystal-lang.org/api/0.25.1/Regex/MatchData.html)
例如,以下代码:
str = "Happy days"
re = /[a-z]+/i
matches = str.match(re)
puts matches

返回 Regex::MatchData("Happy")

我不确定如何将此结果转换为字符串,也不知道为什么这不是默认值,因为在灵感语言(Ruby)中是这样的。我理解这个问题可能是由于我处理结构体和编译语言的经验不足造成的,但我希望得到答案,以便帮助其他来自JS/Ruby背景的人。


看起来你想要 matches.string,但是使用 scan,你将会得到所有匹配。 - Wiktor Stribiżew
3个回答

6
如果我只想将第一个匹配项转换为字符串怎么办?
puts "Happy days"[/[a-z]+/i]?
puts "Happy days".match(/[a-z]+/i).try &.[0]

它将尝试使用 /[a-z]+/i 正则表达式匹配字符串,如果匹配成功,则输出第0组,即整个匹配。请注意,在 [...] 后面的 ? 如果没有找到匹配项,则会优雅地失败。如果仅使用puts "??!!"[/[a-z]+/i],则会引发异常。
请参见此在线演示
如果您想要类似于 String#scan 的功能,用于返回在输入中找到的所有匹配项,则可以使用(根据 @Amadan 的评论只留下缩短版本):
str = "Happy days"
re = /[a-z]+/i
matches = [] of String
str.scan(re) do |match|
      matches << match[0]
    end
puts matches

Output of the code above:

["Happy", "days"]

请注意,String::scan方法会返回一个由每个匹配的Regex::MatchData对象组成的数组。要获取匹配文本,只需访问匹配对象的第一项。
另一种更简短的方法是使用:
matches = str.scan(re).map(&.to_a).flatten

请查看在线演示


1
更简单的写法是:matches = str.scan(re).map(&.string) - Amadan
1
@Amadan 是的,当然,我本来也想加上的,但是睡着了:( 那时已经凌晨2点半了:) - Wiktor Stribiżew
1
如果我只想将第一个匹配转换为字符串怎么办?puts match.string会打印出Happy days,而我只想打印出Happy。同时,puts match会产生Regex::MatchData("Happy") - Shadow43375
@Shadow43375 我在答案顶部添加了两个变体。 - Wiktor Stribiżew

2
实际上,发布的示例在Ruby中返回#<MatchData "Happy">,它也没有“全局”标志-这就是其他人提到的String#scan(Regex)的用途。
如果你只想要一个单一匹配项而不需要通过Regex::MatchData,你可以使用String#[](Regex)
str = "Happy days"
p str[/[a-z]+/i] # => "Happy"

0
正如@Shadow43375在评论中提到的那样,接受的答案只适用于需要第一个结果的情况。
如果想要获取输入中找到的所有匹配项,需要使用以下代码:
matches = str.scan(re).map{ |x| x[0] }

输出:

["Happy", "days"]

Wiktor Stribiżew是错误的,Regex::MatchData.string并不返回匹配的字符串,而是返回原始字符串。

正如他自己演示的那样,输出结果为

matches = str.scan(re).map(&.string)

["Happy days", "Happy days"]

参见 https://crystal-lang.org/api/1.7.2/Regex/MatchData.html#string%3AString-instance-method


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接