So testen Sie mit Ruby, ob eine Zeichenfolge im Grunde eine Ganzzahl in Anführungszeichen ist

128

Ich brauche eine Funktion is_an_integer, wo

  • "12".is_an_integer? gibt true zurück.
  • "blah".is_an_integer? gibt false zurück.

Wie kann ich das in Ruby machen? Ich würde einen regulären Ausdruck schreiben, aber ich gehe davon aus, dass es dafür einen Helfer gibt, den ich nicht kenne.

Tony
quelle
1
Seien Sie vorsichtig mit Lösungen, die auf regulären Ausdrücken basieren. Benchmarks zeigen, dass sie viel langsamer als normaler Code ausgeführt werden.
der Blechmann

Antworten:

135

Sie können reguläre Ausdrücke verwenden. Hier ist die Funktion mit den Vorschlägen von @ janm.

class String
    def is_i?
       !!(self =~ /\A[-+]?[0-9]+\z/)
    end
end

Eine bearbeitete Version laut Kommentar von @wich:

class String
    def is_i?
       /\A[-+]?\d+\z/ === self
    end
end

Falls Sie nur positive Zahlen überprüfen müssen

  if !/\A\d+\z/.match(string_to_check)
      #Is not a positive number
  else
      #Is all good ..continue
  end  
Rado
quelle
4
Nicht schlecht. In Ruby lassen Sie normalerweise das Schlüsselwort "return" weg, wenn der Rückgabewert im letzten Ausdruck der Funktion generiert wird. Dies gibt auch einen ganzzahligen Wert von Null zurück. Sie möchten wahrscheinlich einen Booleschen Wert, also würde so etwas wie !! (str = ~ / ^ [- +]? [0-9] + $ /) dies tun. Dann könnten Sie es zu String hinzufügen und das Argument weglassen, indem Sie "self" anstelle von "str" ​​verwenden, und dann könnten Sie den Namen in "is_i?" ...
Janm
2
Vielen Dank! Ich habe absolut keine Ahnung von Rubinkonventionen und -praktiken. Ich habe gerade schnell nach Ruby und regulären Ausdrücken gesucht, um die Syntax zu sehen, den regulären Ausdruck geändert, um ihn auf das vorliegende Problem anzuwenden, und ihn getestet. Eigentlich ist es ziemlich ordentlich. Ich muss es vielleicht länger ansehen, wenn ich mehr Freizeit habe.
Rado
Sie haben die richtige Idee, aber sie stimmt nicht mit binären oder hexadezimalen Literalen überein (siehe meine bearbeitete Lösung unten).
Sarah Mei
16
Zwei Kommentare. Sie können /regexp/ === selfanstelle des !!(self =~ /regexp/)Konstrukts verwenden. Sie können Zeichenklasse verwenden ‚\ d‘ statt[0-9]
Weichen
1
Die einfachste Regex für eine ganze Zahl ist wahrscheinlich / ^ \ d + $ /
keithxm23
169

Hier ist der einfache Weg:

class String
  def is_integer?
    self.to_i.to_s == self
  end
end

>> "12".is_integer?
=> true
>> "blah".is_integer?
=> false

Ich bin nicht mit den Lösungen einverstanden, die eine Ausnahme zum Konvertieren des Strings hervorrufen - Ausnahmen sind kein Kontrollfluss, und Sie können es genauso gut richtig machen. Meine obige Lösung befasst sich jedoch nicht mit Ganzzahlen, die nicht zur Basis 10 gehören. Hier ist also der Weg, ohne auf Ausnahmen zurückzugreifen:

  class String
    def integer? 
      [                          # In descending order of likeliness:
        /^[-+]?[1-9]([0-9]*)?$/, # decimal
        /^0[0-7]+$/,             # octal
        /^0x[0-9A-Fa-f]+$/,      # hexadecimal
        /^0b[01]+$/              # binary
      ].each do |match_pattern|
        return true if self =~ match_pattern
      end
      return false
    end
  end
Sarah Mei
quelle
27
"01" .to_i.to_s! = "01"
sepp2k
2
Könnten Sie nicht ersetzen self.to_i.to_s == selfmit Integer self rescue false?
Meredith L. Patterson
5
Sie könnten, aber das wäre eine schlechte Form. Sie verwenden keine Ausnahmen als Kontrollfluss, und der Code von niemandem sollte jemals "Rescue False" (oder "Rescue True") enthalten. Ein einfaches Gsub'ing würde meine Lösung für Randfälle funktionieren lassen, die nicht vom OP spezifiziert wurden.
Sarah Mei
4
Ich weiß, dass viele Leute es benutzen, und es ist sicherlich ästhetisch ansprechend. Für mich ist dies jedoch ein Hinweis darauf, dass der Code umstrukturiert werden muss. Wenn Sie eine Ausnahme erwarten, ist dies keine Ausnahme.
Sarah Mei
2
Ich bin damit einverstanden, dass Ausnahmen nicht als Kontrollfluss verwendet werden sollten. Ich denke nicht, dass die Anforderung darin besteht, dass entwicklerorientierte Zahlen erkannt werden. In Situationen ohne Programmierer könnte dies als Fehler angesehen werden, insbesondere angesichts der möglichen Verwirrung um führende Nullen und Oktale. Auch nicht konsistent mit to_i. Ihr Code behandelt den Fall "-0123" nicht. Sobald Sie diesen Fall behandelt haben, benötigen Sie keinen separaten regulären Ausdruck für Oktal. Sie können einfach weiterarbeiten, indem Sie "any?" Verwenden. Die einzige Anweisung in Ihrer Funktion könnte "[/ re1 /, / re2 /, / re3 /] .any? {| Re | self = ~ re}" sein, ohne if-Klauseln oder Rückgaben.
Januar
67

Sie können verwenden Integer(str)und sehen, ob es erhöht:

def is_num?(str)
  !!Integer(str)
rescue ArgumentError, TypeError
  false
end

Es sollte darauf hingewiesen werden, dass dies zwar wahr für ist "01", aber nicht für "09", einfach weil 09dies kein gültiges ganzzahliges Literal wäre. Wenn dies nicht das gewünschte Verhalten ist, können Sie es 10als zweites Argument hinzufügen Integer, sodass die Zahl immer als Basis 10 interpretiert wird.

sepp2k
quelle
39
Alter ... eine Ausnahme provozieren, nur um eine Zahl umzuwandeln? Ausnahmen sind kein Kontrollfluss.
Sarah Mei
29
Sie sind es nicht, aber leider ist dies der kanonische Weg, um die "Ganzheitlichkeit" eines Strings in Ruby zu bestimmen. Die verwendeten Methoden #to_isind wegen ihrer Zulässigkeit einfach zu kaputt.
Avdi
17
Für diejenigen, die sich fragen, warum, ist Integer ("09") nicht gültig, da "0" es oktal macht und 9 keine gültige Oktalzahl ist. osdir.com/ml/lang.ruby.general/2002-08/msg00247.html
Andrew Grimm
20
Sarah: Sie können einen Regex verwenden, aber um alle Fälle zu behandeln, die Ruby beim Parsen von ganzen Zahlen (negative Zahlen, Hex, Oktal, Unterstriche, z. B. 1_000_000) macht, wäre es ein sehr großer Regex und leicht falsch zu verstehen. Integer()ist kanonisch, weil Integer ()Sie sicher wissen, dass alles, was Ruby als ganzzahliges Literal betrachtet, akzeptiert und alles andere abgelehnt wird. Das Duplizieren dessen, was die Sprache Ihnen bereits gibt, ist wohl ein schlechterer Codegeruch als das Verwenden von Ausnahmen zur Kontrolle.
Avdi
9
@Rado So erfindet das Rad neu.
sepp2k
24

Sie können einen Einzeiler machen:

str = ...
int = Integer(str) rescue nil

if int
  int.times {|i| p i}
end

oder auch

int = Integer(str) rescue false

Je nachdem, was Sie versuchen, können Sie auch direkt einen Start-End-Block mit einer Rettungsklausel verwenden:

begin
  str = ...
  i = Integer(str)

  i.times do |j|
    puts j
  end
rescue ArgumentError
  puts "Not an int, doing something else"
end
Robert Klemme
quelle
1
Zum Thema "Ausnahme als Kontrollfluss": Da wir nicht wissen, wie die vorliegende Methode anzuwenden ist, können wir nicht wirklich beurteilen, ob Ausnahmen passen würden oder nicht. Wenn die Zeichenfolge eingegeben wird und eine Ganzzahl sein muss, würde die Angabe einer Nicht-Ganzzahl eine Ausnahme rechtfertigen. Obwohl dann vielleicht die Behandlung nicht in der gleichen Methode ist und wir wahrscheinlich nur Integer (str) .times {| i | machen würden setzt i} oder was auch immer.
Robert Klemme
24
"12".match(/^(\d)+$/)      # true
"1.2".match(/^(\d)+$/)     # false
"dfs2".match(/^(\d)+$/)    # false
"13422".match(/^(\d)+$/)   # true
Maciej Krasowski
quelle
4
Es kehrt nicht zurück trueund falsesondern MatchDataInstanzen undnil
Stefan
Es ist nicht das, was es zurückgibt, aber wenn es passt
Maciej Krasowski
5
Wickeln Sie es mit !!oder verwenden present?Sie es , wenn Sie einen Booleschen Wert benötigen !!( "12".match /^(\d)+$/ )oder "12".match(/^(\d)+$/).present?(letzterer erfordert Rails / Activesupport)
Mahemoff
1
Dieser reguläre Ausdruck berücksichtigt kein Vorzeichen: Negative Zahlen sind ebenfalls gültige Ganzzahlen . Sie testen jetzt auf gültige natürliche Zahlen oder Null.
Jochem Schulenklopper
13

Ruby 2.6.0 ermöglicht das Umwandeln in eine Ganzzahl, ohne eine Ausnahme auszulösen , und kehrt zurück, nilwenn das Umwandeln fehlschlägt. Und da es sich nilmeistens wie falsein Ruby verhält , können Sie leicht nach einer ganzen Zahl suchen:

if Integer(my_var, exception: false)
  # do something if my_var can be cast to an integer
end
Timitry
quelle
8
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. Es wird nicht vorangestellt is_. Ich finde das albern bei Fragezeichenmethoden, ich mag es "04".integer?viel besser als "foo".is_integer?.
  2. Es verwendet die sinnvolle Lösung von sepp2k, die für "01"und so gilt.
  3. Objektorientiert, yay.
August Lilleaas
quelle
1
+1 für die Benennung #integer?, -1 für die Überfüllung der
Zeichenfolge
1
Wo sonst würde es hingehen? integer?("a string")ftl.
August Lilleaas
2
String#integer?ist die Art von allgemeinem Patch, den jeder Ruby-Codierer und sein Cousin gerne zur Sprache hinzufügen, was zu Codebasen mit drei verschiedenen subtil inkompatiblen Implementierungen und unerwartetem Bruch führt. Ich habe das auf die harte Tour bei großen Ruby-Projekten gelernt.
Avdi
Gleicher Kommentar wie oben: Ausnahmen sollten nicht für den Kontrollfluss verwendet werden.
Sarah Mei
Nachteil: Diese Lösung verschwendet eine Konvertierung.
Robert Klemme
7

Der beste und einfachste Weg ist die Verwendung Float

val = Float "234" rescue nil

Float "234" rescue nil #=> 234.0

Float "abc" rescue nil #=> nil

Float "234abc" rescue nil #=> nil

Float nil rescue nil #=> nil

Float "" rescue nil #=> nil

Integerist auch gut, aber es wird 0für zurückkehrenInteger nil

Siva
quelle
Ich bin froh, dass ich deine Antwort bemerkt habe. Sonst wäre ich mit "Integer" gegangen, als ich "Float" brauchte.
Jeff Zivkovic
Das ist einfach, aber die beste Antwort! In den meisten Fällen brauchen wir keinen ausgefallenen Patch für die String-Klasse. Das funktioniert am besten für mich!
Anh Nguyen
(Float (Wert) Rettung falsch)? Float (Wert) .to_s == Wert? Float (Wert): Integer (Wert): Wert
okliv
6

Ich bevorzuge:

config / initializers / string.rb

class String
  def number?
    Integer(self).is_a?(Integer)
  rescue ArgumentError, TypeError
    false
  end
end

und dann:

[218] pry(main)> "123123123".number?
=> true
[220] pry(main)> "123 123 123".gsub(/ /, '').number?
=> true
[222] pry(main)> "123 123 123".number?
=> false

oder überprüfen Sie die Telefonnummer:

"+34 123 456 789 2".gsub(/ /, '').number?
skozz
quelle
4

Ein viel einfacherer Weg könnte sein

/(\D+)/.match('1221').nil? #=> true
/(\D+)/.match('1a221').nil? #=> false
/(\D+)/.match('01221').nil? #=> true
gouravtiwari21
quelle
3
  def isint(str)
    return !!(str =~ /^[-+]?[1-9]([0-9]*)?$/)
  end
Amal Kumar S.
quelle
Nur-Code-Antworten sind nicht sehr nützlich. Erklären Sie stattdessen, wie es funktioniert und warum es die richtige Antwort ist. Wir wollen für die Zukunft aufklären, damit die Lösung verstanden wird, und nicht die unmittelbare Frage lösen.
der Blechmann
3

Persönlich mag ich den Ausnahmeansatz, obwohl ich ihn etwas knapper machen würde:

class String
  def integer?(str)
    !!Integer(str) rescue false
  end
end

Wie andere bereits gesagt haben, funktioniert dies jedoch nicht mit Octal-Strings.

Achtbitraptor
quelle
2

Ruby 2.4 hat Regexp#match?: (mit a ?)

def integer?(str)
  /\A[+-]?\d+\z/.match? str
end

Für ältere Ruby-Versionen gibt es Regexp#===. Und obwohl die direkte Verwendung des Fallgleichheitsoperators generell vermieden werden sollte, sieht er hier sehr sauber aus:

def integer?(str)
  /\A[+-]?\d+\z/ === str
end

integer? "123"    # true
integer? "-123"   # true
integer? "+123"   # true

integer? "a123"   # false
integer? "123b"   # false
integer? "1\n2"   # false
Stefan
quelle
2

Dies ist möglicherweise nicht für alle Fälle geeignet, wenn Sie einfach Folgendes verwenden:

"12".to_i   => 12
"blah".to_i => 0

könnte auch für einige tun.

Wenn es eine Zahl und nicht 0 ist, wird eine Zahl zurückgegeben. Wenn es 0 zurückgibt, ist es entweder eine Zeichenfolge oder 0.

drei
quelle
11
Funktioniert, wird aber seitdem nicht empfohlen "12blah".to_i => 12. Dies kann in seltsamen Szenarien zu Problemen führen.
rfsbraz
2

Hier ist meine Lösung:

# /initializers/string.rb
class String
  IntegerRegex = /^(\d)+$/

  def integer?
    !!self.match(IntegerRegex)
  end
end

# any_model_or_controller.rb
'12345'.integer? # true
'asd34'.integer? # false

Und so funktioniert es:

  • /^(\d)+$/ist ein Regex- Ausdruck zum Suchen von Ziffern in einer beliebigen Zeichenfolge. Sie können Ihre regulären Ausdrücke und Ergebnisse unter http://rubular.com/ testen .
  • Wir speichern es in einer Konstanten IntegerRegex, um unnötige Speicherzuweisungen bei jeder Verwendung in der Methode zu vermeiden.
  • integer?ist eine Abfragemethode, die trueoder zurückgeben sollte false.
  • match ist eine Methode für eine Zeichenfolge, die mit den Vorkommen gemäß dem angegebenen regulären Ausdruck im Argument übereinstimmt und die übereinstimmenden Werte oder zurückgibt nil .
  • !!konvertiert das Ergebnis der matchMethode in einen äquivalenten Booleschen Wert.
  • Das Deklarieren der Methode in einer vorhandenen StringKlasse ist das Patchen von Affen, wodurch nichts an den vorhandenen String-Funktionen geändert wird, sondern lediglich eine weitere Methode hinzugefügt wird, die integer?für ein beliebiges String-Objekt benannt ist.
Sachin
quelle
1
Könnten Sie dem bitte eine kleine Erklärung hinzufügen?
stef
@stef - Ich habe das gleiche getan. Bitte lassen Sie mich wissen, wenn Sie noch Fragen haben.
Sachin
1

Wenn man die Antwort von @ rado oben erweitert, könnte man auch eine ternäre Anweisung verwenden, um die Rückgabe von wahren oder falschen Booleschen Werten ohne die Verwendung von Doppelknallen zu erzwingen. Zugegeben, die Version mit doppelter logischer Negation ist knapper, aber für Neulinge (wie mich) wahrscheinlich schwerer zu lesen.

class String
  def is_i?
     self =~ /\A[-+]?[0-9]+\z/ ? true : false
  end
end
Adeluccar
quelle
Bedenken Sie, dass die Verwendung regulärer Ausdrücke Ruby dazu zwingt, viel mehr Arbeit zu leisten. Wenn dies in einer Schleife verwendet wird, wird der Code verlangsamt. Das Verankern des Ausdrucks hilft, aber Regex sind immer noch deutlich langsamer.
der Blechmann
1

Für allgemeinere Fälle (einschließlich Zahlen mit Dezimalpunkt) können Sie die folgende Methode ausprobieren:

def number?(obj)
  obj = obj.to_s unless obj.is_a? String
  /\A[+-]?\d+(\.[\d]+)?\z/.match(obj)
end

Sie können diese Methode in einer irb-Sitzung testen:

(irb)
>> number?(7)
=> #<MatchData "7" 1:nil>
>> !!number?(7)
=> true
>> number?(-Math::PI)
=> #<MatchData "-3.141592653589793" 1:".141592653589793">
>> !!number?(-Math::PI)
=> true
>> number?('hello world')
=> nil
>> !!number?('hello world')
=> false

Eine ausführliche Erklärung der Regex finden Sie in diesem Blog-Artikel :)

Taiga
quelle
Der Aufruf ist nicht erforderlich, obj.is_a? Stringda sich String # to_s selbst zurückgibt , was im Vergleich zum .is_a?Aufruf vermutlich nicht zu viel Verarbeitung erfordert . Auf diese Weise tätigen Sie in dieser Leitung nur einen Anruf anstelle von einem oder zwei. Sie können auch direkt !!in die number?Methode aufnehmen, da ein Methodenname, der mit endet, gemäß Konvention ?einen booleschen Wert zurückgeben soll. Grüße!
Giovanni Benussi
-1

Ich mag folgendes direkt:

def is_integer?(str)
  str.to_i != 0 || str == '0' || str == '-0'
end

is_integer?('123')
=> true

is_integer?('sdf')
=> false

is_integer?('-123')
=> true

is_integer?('0')
=> true

is_integer?('-0')
=> true

Vorsicht:

is_integer?('123sdfsdf')
=> true
schmijos
quelle
-2

Ein Liner in string.rb

def is_integer?; true if Integer(self) rescue false end
cb24
quelle
-3

Ich bin mir nicht sicher, ob dies der Fall war, als diese Frage gestellt wurde, aber für jeden, der über diesen Beitrag stolpert, ist der einfachste Weg:

var = "12"
var.is_a?(Integer) # returns false
var.is_a?(String) # returns true

var = 12
var.is_a?(Integer) # returns true
var.is_a?(String) # returns false

.is_a? funktioniert mit jedem Objekt.

Neuling
quelle
Das ist nicht das, was die ursprüngliche Frage stellt. OP möchte wissen, ob die Zeichenfolge auch eine Ganzzahl ist. zB "12".is_an_integer? == true "not12".is_an_integer? == false 12.is_an_integer? == true
Marklar