【问题标题】:Validate that string contains only allowed characters in Ruby验证该字符串仅包含 Ruby 中允许的字符
【发布时间】:2017-01-19 18:45:41
【问题描述】:

如何测试一个 Ruby 字符串是否只包含一组特定的字符?

例如,如果我的允许字符集是"AGHTM" 加上数字0-9

  • 字符串"MT3G22AH"有效的

  • 字符串"TAR34" 无效(因为R)。

【问题讨论】:

  • 在询问时,向我们展示您的努力很重要。您是否搜索并没有找到任何东西?然后显示您搜索的位置并解释为什么它没有帮助。你写代码了吗?如果不是,为什么?如果您这样做并且无法使其正常工作,请阅读“minimal reproducible example”。目前,您似乎没有尝试并希望我们为您编写代码,这不是 SO 的用途。 meta.stackoverflow.com/q/261592/128421
  • “MT3G22AH”是整个字符串还是子字符串?
  • @theTinMan 这是整个字符串,并且是真实集合的示例。我还不太了解 reg-ex,也找不到如何通过 SO 或其他资源来实现这种特殊情况。在忙碌之前,我急于解决代码战中类似游戏的挑战,只是为了好玩。感谢您对如何提问的启发。不知道这太可惜了:(希望这个问题不是重复的,如果让我知道的话。
  • 了解 Stack Overflow 是一本参考书,而不是讨论列表,这一点很重要。每个问题及其相关答案都是参考书中的单独文章,并且,为了帮助他人,我们需要问题来获得某些东西。您问了一个问题,但您实际上是在为寻找解决方案的未来用户提出问题。你知道你的系统和你尝试过的东西,但他们不知道,所以对问题的定义越好,它对其他人的帮助就越大。
  • 如果我在 SO 中找到这个问题,我就不会问了。由于一些反对意见或关闭它过于广泛,它仍然不会在该参考书中提供。我自己投票认为它太宽泛了,但仍然找不到重复。

标签: ruby regex


【解决方案1】:

一个非常惯用的非正则表达式解决方案是使用String#count

"MT3G22AH".count("^AGHTM0-9").zero?  # => true
"TAR34".count("^AGHTM0-9").zero?     # => false

如果你觉得它更易读,反之亦然:

"MT3G22AH".count('AGHTM0-9') == "MT3G22AH".size  # => true

任你选。

对于较长的字符串,这两种方法的性能都明显优于基于正则表达式的选项。

【讨论】:

  • 一开始没看明白,因为和我的方法太像了。不过,它似乎确实更快,可能是因为它没有创建新字符串。
  • 为了可读性:string.count('AGHTM0-9') == string.size 可能会更好。它和你的方法一样快。
【解决方案2】:
allowed = "AGHTM"
allowed = /\A[\d#{allowed}]+\z/i

"MT3G22AH" =~ allowed #⇒ truthy
"TAR34" =~ allowed #⇒ falsey

【讨论】:

  • 不错!谢谢。由于我不喜欢 nil0 的回报,我现在正在这样测试:!!('MT3G22AH' =~ allowed)
  • 双重否定是一种快速转换为布尔值的方法。在 Ruby 2.4 中,您也可以根据需要执行 "x".match?(/y/)match? 方法返回一个布尔值。
  • 我希望您的方法在使用长字符串不允许的字符时更快,因为它应该在不匹配后立即停止。 Regex 解决方案似乎总是比删除/计数慢至少 5 倍。
【解决方案3】:

字符串#delete

一种可能性是删除所有允许的字符并检查结果字符串是否为空:

"MT3G22AH".delete("AGHTM0-9").empty?
#=> true
"TAR34".delete("AGHTM0-9").empty?
#=> false

性能

短字符串

对于短字符串,@steenslag 是最快的方法,其次是@Jesse 和我的方法。

def mudasobwa(string)
  allowed = 'AGHTM'
  allowed = /\A[\d#{allowed}]+\z/i
  string.match? allowed
end

def eric(string)
  string.delete('AGHTM1-9').empty?
end

def meagar(string)
  allowed = 'AGHTM0123456789'
  string.chars.uniq.all? { |c| allowed.include?(c) }
end

def jesse(string)
  string.count('^AGHTM0-9').zero?
end

def steenslag(string)
  !string.match?(/[^AGHTM0-9]/) 
end

require 'fruity'

n = 1
str1 = 'MT3G22AH' * n
str2 = 'TAR34' * n
compare do
  _jesse { [jesse(str1), jesse(str2)] }
  _eric { [eric(str1), eric(str2)] }
  _mudasobwa { [mudasobwa(str1), mudasobwa(str2)] }
  _meagar { [meagar(str1), meagar(str2)] }
  _steenslag { [steenslag(str1), steenslag(str2)] }
end

它输出:

Running each test 1024 times. Test will take about 2 seconds.
_steenslag is faster than _jesse by 2.2x ± 0.1
_jesse is faster than _eric by 8.000000000000007% ± 1.0%
_eric is faster than _meagar by 4.3x ± 0.1
_meagar is faster than _mudasobwa by 2.4x ± 0.1

更长的字符串

对于较长的字符串 (n=5000),@Jesse 成为最快的方法。

Running each test 32 times. Test will take about 12 seconds.
_jesse is faster than _eric by 2.5x ± 0.01
_eric is faster than _mudasobwa by 4x ± 1.0
_mudasobwa is faster than _steenslag by 2x ± 0.1
_steenslag is faster than _meagar by 11x ± 0.1

【讨论】:

  • OP 说“MT3G22AH”是字符串大小,因此查看更长的字符串并不能解决问题。不过,它对其他搜索用途很有用。
  • delete 创建另一个数组,只需使用支持相同tr 字符集的count
  • 是的。杰西就是这么做的。
【解决方案4】:

这似乎比之前所有的基准测试都要快(@Eric Duminil)(ruby 2.4):

!string.match?(/[^AGHTM0-9]/) 

【讨论】:

  • 字符串较长,成为第二慢的方法。
  • 正则表达式,尤其是非锚定的,随着字符串变长,速度会急剧下降。
  • 这是不久前的事情,但锚定模式的影响适用。 stackoverflow.com/a/3878656/128421
猜你喜欢
  • 1970-01-01
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多