测试字符串是否是 Ruby on Rails 中的数字
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5661466/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Test if string is a number in Ruby on Rails
提问by Jamie Buchanan
I have the following in my application controller:
我的应用程序控制器中有以下内容:
def is_number?(object)
true if Float(object) rescue false
end
and the following condition in my controller:
以及我的控制器中的以下条件:
if mystring.is_number?
end
The condition is throwing an undefined methoderror. I'm guessing I've defined is_numberin the wrong place...?
条件是抛出undefined method错误。我猜我is_number在错误的地方定义了......?
回答by Jakob S
Create is_number?Method.
创建is_number?方法。
Create a helper method:
创建一个辅助方法:
def is_number? string
true if Float(string) rescue false
end
And then call it like this:
然后像这样调用它:
my_string = '12.34'
is_number?( my_string )
# => true
Extend StringClass.
扩展String类。
If you want to be able to call is_number?directly on the string instead of passing it as a param to your helper function, then you need to define is_number?as an extension of the Stringclass, like so:
如果您希望能够is_number?直接调用字符串而不是将其作为参数传递给您的辅助函数,那么您需要定义is_number?为String类的扩展,如下所示:
class String
def is_number?
true if Float(self) rescue false
end
end
And then you can call it with:
然后你可以调用它:
my_string.is_number?
# => true
回答by hipertracker
class String
def numeric?
return true if self =~ /\A\d+\Z/
true if Float(self) rescue false
end
end
p "1".numeric? # => true
p "1.2".numeric? # => true
p "5.4e-29".numeric? # => true
p "12e20".numeric? # true
p "1a".numeric? # => false
p "1.2.3.4".numeric? # => false
回答by Matt Sanders
Here's a benchmark for common ways to address this problem. Note which one you should use probably depends on the ratio of false cases expected.
这是解决此问题的常用方法的基准。请注意您应该使用哪一个可能取决于预期的错误案例的比率。
- If they are relatively uncommon casting is definitely fastest.
- If false cases are common and you are just checking for ints, comparison vs a transformed state is a good option.
- If false cases are common and you are checking floats, regexp is probably the way to go
- 如果是比较不常见的施法肯定是最快的。
- 如果错误情况很常见并且您只是检查整数,则比较与转换状态是一个不错的选择。
- 如果错误情况很常见并且您正在检查浮点数,则正则表达式可能是要走的路
If performance doesn't matter use what you like. :-)
如果性能无关紧要,请使用您喜欢的。:-)
Integer checking details:
整数检查细节:
# 1.9.3-p448
#
# Calculating -------------------------------------
# cast 57485 i/100ms
# cast fail 5549 i/100ms
# to_s 47509 i/100ms
# to_s fail 50573 i/100ms
# regexp 45187 i/100ms
# regexp fail 42566 i/100ms
# -------------------------------------------------
# cast 2353703.4 (±4.9%) i/s - 11726940 in 4.998270s
# cast fail 65590.2 (±4.6%) i/s - 327391 in 5.003511s
# to_s 1420892.0 (±6.8%) i/s - 7078841 in 5.011462s
# to_s fail 1717948.8 (±6.0%) i/s - 8546837 in 4.998672s
# regexp 1525729.9 (±7.0%) i/s - 7591416 in 5.007105s
# regexp fail 1154461.1 (±5.5%) i/s - 5788976 in 5.035311s
require 'benchmark/ips'
int = '220000'
bad_int = '22.to.2'
Benchmark.ips do |x|
x.report('cast') do
Integer(int) rescue false
end
x.report('cast fail') do
Integer(bad_int) rescue false
end
x.report('to_s') do
int.to_i.to_s == int
end
x.report('to_s fail') do
bad_int.to_i.to_s == bad_int
end
x.report('regexp') do
int =~ /^\d+$/
end
x.report('regexp fail') do
bad_int =~ /^\d+$/
end
end
Float checking details:
浮动检查细节:
# 1.9.3-p448
#
# Calculating -------------------------------------
# cast 47430 i/100ms
# cast fail 5023 i/100ms
# to_s 27435 i/100ms
# to_s fail 29609 i/100ms
# regexp 37620 i/100ms
# regexp fail 32557 i/100ms
# -------------------------------------------------
# cast 2283762.5 (±6.8%) i/s - 11383200 in 5.012934s
# cast fail 63108.8 (±6.7%) i/s - 316449 in 5.038518s
# to_s 593069.3 (±8.8%) i/s - 2962980 in 5.042459s
# to_s fail 857217.1 (±10.0%) i/s - 4263696 in 5.033024s
# regexp 1383194.8 (±6.7%) i/s - 6884460 in 5.008275s
# regexp fail 723390.2 (±5.8%) i/s - 3613827 in 5.016494s
require 'benchmark/ips'
float = '12.2312'
bad_float = '22.to.2'
Benchmark.ips do |x|
x.report('cast') do
Float(float) rescue false
end
x.report('cast fail') do
Float(bad_float) rescue false
end
x.report('to_s') do
float.to_f.to_s == float
end
x.report('to_s fail') do
bad_float.to_f.to_s == bad_float
end
x.report('regexp') do
float =~ /^[-+]?[0-9]*\.?[0-9]+$/
end
x.report('regexp fail') do
bad_float =~ /^[-+]?[0-9]*\.?[0-9]+$/
end
end
回答by Damien MATHIEU
Relying on the raised exception is not the fastest, readable nor reliable solution.
I'd do the following :
依赖引发的异常不是最快、可读或可靠的解决方案。
我会做以下事情:
my_string.should =~ /^[0-9]+$/
回答by antpaw
this is how i do it, but i think too there must be a better way
这就是我的做法,但我也认为一定有更好的方法
object.to_i.to_s == object || object.to_f.to_s == object
回答by Timitry
As of Ruby 2.6.0, the numeric cast-methods have an optional exception-argument [1]. This enables us to use the built-in methods without using exceptions as control flow:
从 Ruby 2.6.0 开始,数字转换方法有一个可选的exception-argument [1]。这使我们能够在不使用异常作为控制流的情况下使用内置方法:
Float('x') # => ArgumentError (invalid value for Float(): "x")
Float('x', exception: false) # => nil
Therefore, you don't have to define your own method, but can directly check variables like e.g.
因此,您不必定义自己的方法,但可以直接检查变量,例如
if Float(my_var, exception: false)
# do something if my_var is a float
end
回答by pthamm
Tl;dr:Use a regex approach. It is 39x faster than the rescue approach in the accepted answer and also handles cases like "1,000"
Tl; dr:使用正则表达式方法。它比接受的答案中的救援方法快 39 倍,并且还可以处理“1,000”之类的情况
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
--
——
The accepted answer by @Jakob S works for the most part, but catching exceptions can be really slow. In addition, the rescue approach fails on a string like "1,000".
@Jakob S 接受的答案在大多数情况下都有效,但捕获异常可能真的很慢。此外,救援方法在像“1,000”这样的字符串上失败。
Let's define the methods:
让我们定义这些方法:
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
And now some test cases:
现在有一些测试用例:
test_cases = {
true => ["5.5", "23", "-123", "1,234,123"],
false => ["hello", "99designs", "(123)456-7890"]
}
And a little code to run the test cases:
还有一些运行测试用例的代码:
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
Here is the output of the test cases:
以下是测试用例的输出:
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
Time to do some performance benchmarks:
是时候做一些性能基准测试了:
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
And the results:
结果:
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
回答by corroded
no you're just using it wrong. your is_number? has an argument. you called it without the argument
不,你只是用错了。你的is_number?有一个论点。你在没有参数的情况下调用它
you should be doing is_number?(mystring)
你应该做 is_number?(mystring)
回答by jcye
In rails 4, you need to put
require File.expand_path('../../lib', __FILE__) + '/ext/string'in your config/application.rb
在 Rails 4 中,您需要放入
require File.expand_path('../../lib', __FILE__) + '/ext/string'config/application.rb
回答by Mark Schneider
If you prefer not to use exceptions as part of the logic, you might try this:
如果您不想将异常用作逻辑的一部分,您可以试试这个:
class String
def numeric?
!!(self =~ /^-?\d+(\.\d*)?$/)
end
end
Or, if you want it to work across all object classes, replace class Stringwith class Objectan convert self to a string: !!(self.to_s =~ /^-?\d+(\.\d*)?$/)
或者,如果你想让它在所有对象类的工作,更换class String用class Object的转换自转换为字符串: !!(self.to_s =~ /^-?\d+(\.\d*)?$/)

