Testen Sie, ob die Zeichenfolge in Ruby on Rails eine Zahl ist

Ich habe folgendes in meinem Anwendungscontroller:

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

und die folgende Bedingung in meinem Controller:

 if mystring.is_number? end 

Die Bedingung verursacht einen nicht undefined method . Ich is_number ich habe is_number am falschen Ort …?

Erstellen Sie is_number? Methode.

Erstellen Sie eine Hilfsmethode:

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

Und dann nenne es so:

 my_string = '12.34' is_number?( my_string ) # => true 

String class erweitern

Wenn Sie is_number? anrufen is_number? direkt auf der Zeichenkette, anstatt es als Parameter an Ihre Hilfsfunktion zu übergeben, dann müssen Sie is_number? definieren is_number? als eine Erweiterung der String class, wie folgt:

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

Und dann kannst du es nennen mit:

 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 

Hier finden Sie einen Richtwert für gängige Methoden zur Behebung dieses Problems. Hinweis, welche Sie verwenden sollten, hängt wahrscheinlich vom Verhältnis der erwarteten falschen Fälle ab.

  1. Wenn sie relativ selten sind, ist das Gießen definitiv am schnellsten.
  2. Wenn falsche Fälle üblich sind und Sie nur nach Ints suchen, ist der Vergleich mit einem transformierten Zustand eine gute Option.
  3. Wenn falsche Fälle üblich sind und Sie Floats überprüfen, ist Regexp wahrscheinlich der Weg zu gehen

Wenn performance nicht wichtig ist, verwenden Sie, was Sie möchten. 🙂

Integer-Prüfdetails:

 # 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 

Details zur Floatprüfung:

 # 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 

Sich auf die erhobene Ausnahme zu verlassen, ist nicht die schnellste, lesbarste oder zuverlässigste Lösung.
Ich würde Folgendes tun:

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

Nein, du benutzt es nur falsch. deine is_number? hat ein Argument. Du hast es ohne das Argument genannt

du solltest is_number machen? (mystring)

Tl; dr: Verwenden Sie einen Regex-Ansatz. Es ist 39x schneller als der Rettungsansatz in der akzeptierten Antwort und behandelt auch Fälle wie “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 

Die akzeptierte Antwort von @Jakob S funktioniert größtenteils, aber das Abfangen von Ausnahmen kann sehr langsam sein. Außerdem schlägt der Rettungsansatz bei einer Zeichenfolge wie “1000” fehl.

Lassen Sie uns die Methoden definieren:

 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 

Und jetzt einige Testfälle:

 test_cases = { true => ["5.5", "23", "-123", "1,234,123"], false => ["hello", "99designs", "(123)456-7890"] } 

Und ein kleiner Code zum Ausführen der Testfälle:

 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 

Hier ist die Ausgabe der Testfälle:

 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 

Zeit für einige 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 

Und die Ergebnisse:

 Calculating ------------------------------------- rescue 128.000 i/100ms regex 4.649ki/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 

So mache ich das, aber ich denke auch, dass es einen besseren Weg geben muss

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

In Rails 4 müssen Sie in require File.expand_path('../../lib', __FILE__) + '/ext/string' config / application.rb den require File.expand_path('../../lib', __FILE__) + '/ext/string'

Wenn Sie Ausnahmen nicht als Teil der Logik verwenden möchten, können Sie Folgendes versuchen:

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

Wenn Sie möchten, dass es über alle Objektklassen hinweg funktioniert, ersetzen Sie die class String durch die class Object convert self durch eine Zeichenfolge: !!(self.to_s =~ /^-?\d+(\.\d*)?$/)

Verwenden Sie die folgende function:

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

damit,

is_numeric? "1.2f" is_numeric? "1.2f" = falsch

is_numeric? "1.2" is_numeric? "1.2" = wahr

is_numeric? "12f" is_numeric? "12f" = falsch

is_numeric? "12" is_numeric? "12" = wahr

Wie dumm ist diese Lösung?

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