Проверьте, является ли строка числом в Ruby on Rails



у меня есть следующее В моем контроллере приложений:



def is_number?(object)
true if Float(object) rescue false
end


и следующее условие в моем контроллере:



if mystring.is_number?

end


условие выдачи undefined method ошибка. Я предполагаю, что я определил is_number в неположенном месте...?

916   11  

11 ответов:

создать is_number? метод.

создать вспомогательный метод:

def is_number? string
  true if Float(string) rescue false
end

а потом назовем это так:

my_string = '12.34'

is_number?( my_string )
# => true

расширения String класса.

если вы хотите иметь возможность позвонить is_number? непосредственно на строку вместо того, чтобы передавать его в качестве параметра для вашей вспомогательной функции, то вам нужно определить is_number? как продолжение String класс, вот так:

class String
  def is_number?
    true if Float(self) rescue false
  end
end

и затем вы можете вызвать его с:

my_string.is_number?
# => true
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

вот эталон для общих способов решения этой проблемы. Обратите внимание, что вы должны использовать, вероятно, зависит от соотношения ожидаемых ложных случаев.

  1. если они относительно необычны, кастинг, безусловно, самый быстрый.
  2. если ложные случаи распространены, и вы просто проверяете ints, сравнение с преобразованным состоянием-хороший вариант.
  3. если ложные случаи распространены, и вы проверяете поплавки, регулярное выражение, вероятно, является способом иди

Если производительность не имеет значения, использовать то, что вам нравится. : -)

целое число проверка реквизитам:

# 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

детали проверки поплавка:

# 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

полагаться на вызванное исключение-это не самое быстрое, читаемое и надежное решение.
Я бы сделал следующее :

my_string.should =~ /^[0-9]+$/

нет, вы просто используете его неправильно. ваш is_number? имеет аргумент. вы назвали его без аргумента

вы должны делать is_number?(mystring)

Tl; dr: используйте метод регулярных выражений. Это в 39 раз быстрее, чем подход спасения в принятом ответе, а также обрабатывает такие случаи, как "1000"

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

--

принятый ответ @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

время, чтобы сделать некоторые контрольные показатели:

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

вот как я это делаю, но я тоже думаю, что должен быть лучший способ

object.to_i.to_s == object || object.to_f.to_s == object

в рельсы 4, вам нужно положить require File.expand_path('../../lib', __FILE__) + '/ext/string' в вашей конфигурации / приложении.РБ

если вы предпочитаете не использовать исключения как часть логики, вы могли бы попробовать это:

class String
   def numeric?
    !!(self =~ /^-?\d+(\.\d*)?$/)
  end
end

или, если вы хотите, чтобы он работал во всех классах объектов, замените class String С class Object преобразование себя в строку:!!(self.to_s =~ /^-?\d+(\.\d*)?$/)

использовать следующую функцию:

def is_numeric? val
    return val.try(:to_f).try(:to_s) == val
end

и

is_numeric? "1.2f" = false

is_numeric? "1.2" = true

is_numeric? "12f" = false

is_numeric? "12" = true

насколько глупо это решение?

def is_number?(i)
  begin
    i+0 == i
  rescue TypeError
    false
  end
end

Comments

    Ничего не найдено.