Проверьте, является ли строка числом в Ruby on Rails
у меня есть следующее В моем контроллере приложений:
def is_number?(object)
true if Float(object) rescue false
end
и следующее условие в моем контроллере:
if mystring.is_number?
end
условие выдачи undefined method ошибка. Я предполагаю, что я определил is_number в неположенном месте...?
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
вот эталон для общих способов решения этой проблемы. Обратите внимание, что вы должны использовать, вероятно, зависит от соотношения ожидаемых ложных случаев.
- если они относительно необычны, кастинг, безусловно, самый быстрый.
- если ложные случаи распространены, и вы просто проверяете ints, сравнение с преобразованным состоянием-хороший вариант.
- если ложные случаи распространены, и вы проверяете поплавки, регулярное выражение, вероятно, является способом иди
Если производительность не имеет значения, использовать то, что вам нравится. : -)
целое число проверка реквизитам:
# 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
Comments