Ruby on rails 在RubyonRails中测试字符串是否为数字
我的应用程序控制器中有以下内容:Ruby on rails 在RubyonRails中测试字符串是否为数字,ruby-on-rails,ruby,string,integer,Ruby On Rails,Ruby,String,Integer,我的应用程序控制器中有以下内容: def is_number?(object) true if Float(object) rescue false end if mystring.is_number? end 以及控制器中的以下情况: def is_number?(object) true if Float(object) rescue false end if mystring.is_number? end 该条件正在抛出一个未定义的方法错误。我猜我定义的是不是在错误的地
def is_number?(object)
true if Float(object) rescue false
end
if mystring.is_number?
end
以及控制器中的以下情况:
def is_number?(object)
true if Float(object) rescue false
end
if mystring.is_number?
end
该条件正在抛出一个
未定义的方法
错误。我猜我定义的是不是在错误的地方…?不,你只是用错了。你的电话号码是多少?他有一个论点。你没有辩论就说了
你应该做的是什么?(mystring)我就是这样做的,但我认为一定有更好的方法
object.to_i.to_s == object || object.to_f.to_s == object
创建is\u number?
方法。
创建助手方法:
def is_number? string
true if Float(string) rescue false
end
然后这样称呼它:
my_string = '12.34'
is_number?( my_string )
# => true
扩展字符串
类。
如果希望能够直接在字符串上调用is\u number?
,而不是将其作为参数传递给助手函数,则需要将is\u number?
定义为string
类的扩展,如下所示:
class String
def is_number?
true if Float(self) rescue false
end
end
然后你可以用以下方式来称呼它:
my_string.is_number?
# => true
依赖引发的异常不是最快、可读性和可靠性的解决方案。
我会做以下几件事:
my_string.should =~ /^[0-9]+$/
如果您不希望将异常作为逻辑的一部分,可以尝试以下方法:
class String
def numeric?
!!(self =~ /^-?\d+(\.\d*)?$/)
end
end
或者,如果您希望它能够跨所有对象类工作,请将类字符串
替换为类对象
转换为字符串:!!(self.to_s=~/^-?\d+(\.\d*)?$/)
这个解决方案有多愚蠢
def is_number?(i)
begin
i+0 == i
rescue TypeError
false
end
end
以下是解决此问题的常用方法的基准。请注意,您应该使用哪一种可能取决于预期的错误案例的比率
如果他们是相对不常见的铸造肯定是最快的
如果错误情况很常见,并且您只是在检查int,那么比较与转换状态是一个不错的选择
如果错误的情况很常见,并且您正在检查浮动,那么regexp可能是一种方法
如果性能不重要,请使用您喜欢的工具。:-)
整数检查详细信息:
浮动检查详细信息:
在rails 4中,您需要
require File.expand_path('../../lib',uuu File++'/ext/string'
在config/application.rb中使用以下函数:
def is_numeric? val
return val.try(:to_f).try(:to_s) == val
end
所以
是数字吗?“1.2f”
=错误
是数字吗?“1.2”
=真
是数字吗?“12f”
=错误
是数字吗?“12”
=trueTl;dr:使用正则表达式方法。在公认的答案中,它比救援方法快39倍,还可以处理“1000”之类的情况
--
@Jakob S接受的答案在很大程度上是有效的,但是捕获异常的速度可能非常慢。此外,救援方法在“1000”这样的字符串上失败
让我们定义方法:
def rescue_is_number? string
true if Float(string) rescue false
end
def regex_is_number? string
no_commas = string.gsub(',', '')
matches = no_commas.match(/-?\d+(?:\.\d+)?/)
if !matches.nil? && matches.size == 1 && matches[0] == no_commas
true
else
false
end
end
现在是一些测试用例:
test_cases = {
true => ["5.5", "23", "-123", "1,234,123"],
false => ["hello", "99designs", "(123)456-7890"]
}
test_cases.each do |expected_answer, cases|
cases.each do |test_case|
if rescue_is_number?(test_case) != expected_answer
puts "**rescue_is_number? got #{test_case} wrong**"
else
puts "rescue_is_number? got #{test_case} right"
end
if regex_is_number?(test_case) != expected_answer
puts "**regex_is_number? got #{test_case} wrong**"
else
puts "regex_is_number? got #{test_case} right"
end
end
end
rescue_is_number? got 5.5 right
regex_is_number? got 5.5 right
rescue_is_number? got 23 right
regex_is_number? got 23 right
rescue_is_number? got -123 right
regex_is_number? got -123 right
**rescue_is_number? got 1,234,123 wrong**
regex_is_number? got 1,234,123 right
rescue_is_number? got hello right
regex_is_number? got hello right
rescue_is_number? got 99designs right
regex_is_number? got 99designs right
rescue_is_number? got (123)456-7890 right
regex_is_number? got (123)456-7890 right
还有一些运行测试用例的代码:
test_cases = {
true => ["5.5", "23", "-123", "1,234,123"],
false => ["hello", "99designs", "(123)456-7890"]
}
test_cases.each do |expected_answer, cases|
cases.each do |test_case|
if rescue_is_number?(test_case) != expected_answer
puts "**rescue_is_number? got #{test_case} wrong**"
else
puts "rescue_is_number? got #{test_case} right"
end
if regex_is_number?(test_case) != expected_answer
puts "**regex_is_number? got #{test_case} wrong**"
else
puts "regex_is_number? got #{test_case} right"
end
end
end
rescue_is_number? got 5.5 right
regex_is_number? got 5.5 right
rescue_is_number? got 23 right
regex_is_number? got 23 right
rescue_is_number? got -123 right
regex_is_number? got -123 right
**rescue_is_number? got 1,234,123 wrong**
regex_is_number? got 1,234,123 right
rescue_is_number? got hello right
regex_is_number? got hello right
rescue_is_number? got 99designs right
regex_is_number? got 99designs right
rescue_is_number? got (123)456-7890 right
regex_is_number? got (123)456-7890 right
以下是测试用例的输出:
test_cases = {
true => ["5.5", "23", "-123", "1,234,123"],
false => ["hello", "99designs", "(123)456-7890"]
}
test_cases.each do |expected_answer, cases|
cases.each do |test_case|
if rescue_is_number?(test_case) != expected_answer
puts "**rescue_is_number? got #{test_case} wrong**"
else
puts "rescue_is_number? got #{test_case} right"
end
if regex_is_number?(test_case) != expected_answer
puts "**regex_is_number? got #{test_case} wrong**"
else
puts "regex_is_number? got #{test_case} right"
end
end
end
rescue_is_number? got 5.5 right
regex_is_number? got 5.5 right
rescue_is_number? got 23 right
regex_is_number? got 23 right
rescue_is_number? got -123 right
regex_is_number? got -123 right
**rescue_is_number? got 1,234,123 wrong**
regex_is_number? got 1,234,123 right
rescue_is_number? got hello right
regex_is_number? got hello right
rescue_is_number? got 99designs right
regex_is_number? got 99designs right
rescue_is_number? got (123)456-7890 right
regex_is_number? got (123)456-7890 right
是时候做一些性能基准测试了:
Benchmark.ips do |x|
x.report("rescue") { test_cases.values.flatten.each { |c| rescue_is_number? c } }
x.report("regex") { test_cases.values.flatten.each { |c| regex_is_number? c } }
x.compare!
end
结果是:
Calculating -------------------------------------
rescue 128.000 i/100ms
regex 4.649k i/100ms
-------------------------------------------------
rescue 1.348k (±16.8%) i/s - 6.656k
regex 52.113k (± 7.8%) i/s - 260.344k
Comparison:
regex: 52113.3 i/s
rescue: 1347.5 i/s - 38.67x slower
从Ruby 2.6.0开始,数值转换方法有一个可选的exception
-参数。这使我们能够使用内置方法,而不使用异常作为控制流:
Float('x') # => ArgumentError (invalid value for Float(): "x")
Float('x', exception: false) # => nil
因此,您不必定义自己的方法,但可以直接检查变量,例如
if Float(my_var, exception: false)
# do something if my_var is a float
end
因为,可以用来验证字符串的数字性,所以我唯一可以添加的是它的一个线性版本,而不使用rescue
块来控制流(这有时被认为是一种不好的做法)
Float(我的字符串,异常:false)。是否存在?
基于is\U编号?问题中的方法,使用is_a?没有给出正确的答案。如果mystring
确实是一个字符串,mystring.is_a?(整数)
将始终为false。看起来他想要的结果是is_number?(“12.4”)=>true
Jakob S是正确的。mystring实际上总是一个字符串,但可能只是由数字组成。也许我的问题应该是数字?为了避免混淆数据类型,这只适用于正整数。“-1”、“0.0”或“1_000”等值都返回false,即使它们是有效的数值。您看到的是类似于/^[-.0-9]+$/,但它错误地接受了“--”。来自Rails的“验证了:raw_value.to_s=~/\A[+-]?\d+\Z/NoMethodError:undefined method`should'”:string在最新的rspec中,它变成了expect(my_string)。要匹配(/^[0-9]+$/)
I like:my_string=~/\A+(\d-)?\。?\d+\Z/
它允许您执行“.1”、“-0.1”或“12”,但不允许执行“、-”或“.”这是个坏主意。“330.346.11.”to#f=>330.346上面没有to#f
,Float()不会表现出这种行为:Float(“330.346.11”)
引发ArgumentError:Float()的值无效:“330.346.11”
如果使用该补丁,我会将其重命名为numeric?,以符合ruby命名约定(数值类继承自Numeric,is.\ux前缀是javaish)。这与最初的问题不太相关,但我可能会将代码放在lib/core\u ext/string.rb
中。我不认为是数值?(string)
位工作于Ruby 1.9。也许这是Rails或1.8的一部分?string。是吗?(数值)
有效。另请参见。它不识别浮点表示法,例如1.2e+35。在Ruby 2.4.0中,我运行了object=“1.2e+35”;object.to_f.to_s==object
而且它能工作我知道很多人来这里是因为codeschool的Rails for Zombies测试课。只要等他继续解释。测试不应该通过——让你测试出错是可以的,你总是可以修补Rails来发明诸如self之类的方法。你知道吗?公认的答案是失败的像“1000”这样的情况比使用正则表达式慢39倍。请参见下面的答案。这是次优的,因为使用“.respond_to”(:+)”总是比在特定方法上失败并捕获异常要好(:+)这也可能会失败,原因有很多,比如正则表达式和转换方法没有。实际上你不需要这样做,你可以把string.rb放在“initializers”中,然后