测试字符串是否是 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:55:53  来源:igfitidea点击:

Test if string is a number in Ruby on Rails

ruby-on-railsrubystringinteger

提问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.

这是解决此问题的常用方法的基准。请注意您应该使用哪一个可能取决于预期的错误案例的比率。

  1. If they are relatively uncommon casting is definitely fastest.
  2. If false cases are common and you are just checking for ints, comparison vs a transformed state is a good option.
  3. If false cases are common and you are checking floats, regexp is probably the way to go
  1. 如果是比较不常见的施法肯定是最快的。
  2. 如果错误情况很常见并且您只是检查整数,则比较与转换状态是一个不错的选择。
  3. 如果错误情况很常见并且您正在检查浮点数,则正则表达式可能是要走的路

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 Stringclass Object的转换自转换为字符串: !!(self.to_s =~ /^-?\d+(\.\d*)?$/)