J'ai les éléments suivants dans mon contrôleur d'application:
def is_number?(object)
true if Float(object) rescue false
end
et la condition suivante dans mon contrôleur:
if mystring.is_number?
end
La condition jette un undefined method
Erreur. Je suppose que j'ai défini is_number
au mauvais endroit ...?
is_number?
Méthode.Créez une méthode d'assistance:
def is_number? string
true if Float(string) rescue false
end
Et puis appelez ça comme ça:
my_string = '12.34'
is_number?( my_string )
# => true
String
Classe.Si vous voulez pouvoir appeler is_number?
directement sur la chaîne au lieu de la passer comme paramètre à votre fonction d’aide, vous devez définir is_number?
comme une extension de la classe String
, comme ceci:
class String
def is_number?
true if Float(self) rescue false
end
end
Et puis vous pouvez l'appeler avec:
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
Voici une référence pour les moyens courants de résoudre ce problème. Notez que celui que vous devriez utiliser dépend probablement du nombre de faux cas attendus.
Si les performances ne comptent pas, utilisez ce que vous aimez. :-)
# 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
S'appuyer sur l'exception levée n'est pas la solution la plus rapide, lisible ni fiable.
Je ferais ce qui suit:
my_string.should =~ /^[0-9]+$/
Tl; dr: Utilisez une approche regex. Il est 39 fois plus rapide que l'approche de secours dans la réponse acceptée et traite également des cas du type "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
-
La réponse acceptée par @Jobob S fonctionne pour la plupart, mais la détection des exceptions peut être très lente. En outre, l'approche de secours échoue sur une chaîne telle que "1 000".
Définissons les méthodes:
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
Et maintenant quelques cas de test:
test_cases = {
true => ["5.5", "23", "-123", "1,234,123"],
false => ["hello", "99designs", "(123)456-7890"]
}
Et un peu de code pour exécuter les cas de test:
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
Voici le résultat des cas de test:
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
Il est temps de faire quelques points de repère de performance:
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
Et les résultats:
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
non, vous l'utilisez mal. votre is_number? a un argument. vous l'avez appelé sans argument
vous devriez faire is_number? (mystring)
Dans Rails 4, vous devez mettre require File.expand_path('../../lib', __FILE__) + '/ext/string'
dans votre config/application.rb
voici comment je le fais, mais je pense aussi qu'il doit y avoir un meilleur moyen
object.to_i.to_s == object || object.to_f.to_s == object
Si vous préférez ne pas utiliser les exceptions dans le cadre de la logique, essayez ceci:
class String
def numeric?
!!(self =~ /^-?\d+(\.\d*)?$/)
end
end
Ou, si vous souhaitez que cela fonctionne sur toutes les classes d'objets, remplacez class String
Par class Object
Et convertissez-le en chaîne: !!(self.to_s =~ /^-?\d+(\.\d*)?$/)
À partir de Ruby 2.6.0, les méthodes de conversion numériques ont un argument optionnel exception
- [1] . Cela nous permet d'utiliser le -in méthodes sans utiliser d'exceptions comme flux de contrôle:
Float('x') # => ArgumentError (invalid value for Float(): "x")
Float('x', exception: false) # => nil
Par conséquent, vous n'avez pas à définir votre propre méthode, mais vous pouvez directement vérifier des variables telles que, par exemple.
if Float(my_var, exception: false)
# do something if my_var is a float
end