Ruby中的不区分大小写正则表达式匹配

5

我有一个包含所有 Power Rangers 颜色的数组:rangers = ["red", "blue", "yellow", "pink", "black"]

我想验证给定的参数是否匹配 Power Rangers 的颜色,但问题是参数可能以不同的大小写形式出现(大写、小写或混合)。

例如:

def validate_rangers(color)
  rangers = ["red", "blue", "yellow", "pink", "black"]
  rangers.grep(color).any?
end

validates_rangers("red") #=> true

但是
validates_rangers("Red") #=> false. Needs to be true.

我该如何使用不区分大小写的grep命令?

1
回答实际问题,i是不区分大小写的标志,因此正则表达式应为/#{color}/i 示例 - engineersmnky
5个回答

21

你可以使用“insensitive”标志:

rangers.grep(/#{color}/i).any?

由于标志将正则表达式设置为不区分大小写,它将匹配red中的任何情况。

演示链接

def validate_rangers(color)
  rangers = ["red", "blue", "yellow", "pink", "black"]
  rangers.grep(/#{color}/i).any?
end

8
这个问题更符合 Ruby 的惯用方式,代码如下:
# Define a constant that defines the colors once and once only.
RANGERS = ["red", "blue", "yellow", "pink", "black"]

def validates_rangers(color)
  # Check against an arbitrary object that may or may not be a string,
  # and downcase it to match better.
  RANGERS.include?(color.to_s.downcase)
end

如果您经常这样做,您可能需要使用Set来优化性能:

RANGERS = Set.new(["red", "blue", "yellow", "pink", "black"])

这不需要进行任何代码更改,但查找速度会更快。

如果您坚持使用正则表达式:

# Construct a case-insensitive regular expression that anchors to the
# beginning and end of the string \A...\z
RANGERS = Regexp.new(
  '\A(?:%s)\z' % Regexp.union("red", "blue", "yellow", "pink", "black"),
  Regexp::IGNORECASE
)

def validates_rangers(color)
  # Double negation returns true/false instead of MatchData
  !!RANGERS.match(color.to_s.downcase)
end

validates_rangers("Red")
# => true
validates_rangers("dred")
# => false

虽然这个实现对于给定的示例似乎有点极端,但它绝对是一个值得贡献的实现,添加 to_s 来避免数据类型问题提供了一个很好的补充。 - engineersmnky
可爱的正则表达式结构。 - Aleksei Matiushkin
除非您已经有一个数组,否则您可以直接使用文字表达式,即 RANGERS = /\A(?:red|blue|yellow|pink|black)\z/i。由于它是一个不区分大小写的正则表达式,所以您可以简单地编写 !!RANGERS.match(color)(并且很快 RANGERS.match?(color))。 - Stefan
@Stefan 是的,从技术上讲是正确的,但有时候将其作为中间数组会更好,这样你就可以更容易地将其抽象化,以便从JSON、YAML或其他形式的配置文件中读取,而只需进行最小的更改。当我看到这样的列表时,我会认为这是一个经常会发生变化的列表。 - tadman

3
你可以在参数中使用 downcase。
def validate_rangers(color)
  rangers = ["red", "blue", "yellow", "pink", "black"]
  rangers.include?(color.downcase)
end

0

Array.grep使用===进行比较。如果要实际使用正则表达式作为比较,您可以将数组更改为正则表达式而不是字符串:

  rangers = [/red/, /blue/, /yellow/, /pink/, /black/]

然后在你的比较中,使用正则表达式匹配(=~)而不是====:

 rangers.map{|ranger| ranger =~ color }.any?

为了进行不区分大小写的比较,您可以在正则表达式值的末尾添加 i。所有内容放在一起,它会像这样:

def validate_rangers(color)
  rangers = [/red/i, /blue/i, /yellow/i, /pink/i, /black/i]
  rangers.map{|ranger| ranger =~ color }.any?
end

“map” 是多余的, “any?” 本身就带有块。另外,“rangers = %w|red blue yellow pink black|.map { |w| /#{w}/i }” 看起来不太像 PHP。 - Aleksei Matiushkin
这里使用多个正则表达式实在是有些过头了。提示:Regexp.union - tadman

0

这里不需要正则表达式:

def validate_rangers(color)
  rangers = %w|red blue yellow pink black|
  rangers.any? &color.downcase.method(:==)
end

为什么不使用findinclude呢? - tadman
@tadman,“find”在技术上不是布尔响应“nil”或第一个对象。虽然当作为条件语句使用时,由于Ruby的真/假实现,它仍将作为布尔值运行。另一方面,“include?”我同意。 - engineersmnky
1
@tadman 没有任何原因: 已经有使用 find/include? 方法的答案了,我展示了一个更优雅(读作:不太可读)的方法。我几乎可以确定,像上面那样调用 proc 更快。 - Aleksei Matiushkin

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