Ruby kullanarak bir dizenin temelde tırnak içinde bir tam sayı olup olmadığı nasıl test edilir?


128

Bir işleve ihtiyacım var is_an_integer, nerede

  • "12".is_an_integer? true döndürür.
  • "blah".is_an_integer? yanlış döndürür.

Bunu Ruby'de nasıl yapabilirim? Bir normal ifade yazarım ama bunun için farkında olmadığım bir yardımcı olduğunu varsayıyorum.



1
Normal ifadelere dayanan çözümleri kullanırken dikkatli olun. Karşılaştırmalar, normal koddan çok daha yavaş çalıştıklarını gösteriyor.
The Tin Man

Yanıtlar:


135

Normal ifadeler kullanabilirsiniz. İşte @ janm'ın önerileri ile işlev.

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

@Wich'in yorumuna göre düzenlenmiş bir sürüm:

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

Yalnızca pozitif sayıları kontrol etmeniz gerektiğinde

  if !/\A\d+\z/.match(string_to_check)
      #Is not a positive number
  else
      #Is all good ..continue
  end  

4
Fena değil. Ruby'de, işlevdeki son ifadede dönüş değeri oluşturulmuşsa, genellikle "return" anahtar sözcüğünü atlarsınız. Bu aynı zamanda sıfırın tamsayı değerini de döndürecektir, muhtemelen bir boole istiyorsunuz, yani !! (str = ~ / ^ [- +]? [0-9] + $ /) gibi bir şey bunu yapacaktır. Daha sonra bunu String'e ekleyebilir ve "str" ​​yerine "self" kullanarak bağımsız değişkeni bırakabilir ve ardından adı "is_i?" Olarak değiştirebilirsiniz. ...
janm

2
Teşekkürler! Ruby gelenekleri ve uygulamaları hakkında kesinlikle hiçbir fikrim yok. Sözdizimini görmek için ruby ​​ve normal ifadelerde hızlı bir google yaptım, mevcut soruna uygulamak için normal ifadeyi değiştirdim ve test ettim. Aslında oldukça temiz .. Daha fazla boş zamanım olduğunda daha uzun süre bakmak zorunda kalabilirim.
Rado

Doğru fikre sahipsiniz, ancak ikili veya onaltılık değişmez değerlerle eşleşmiyor (aşağıdaki düzenlenmiş çözümüme bakın).
Sarah Mei

16
İki yorum. Yapı /regexp/ === selfyerine kullanabilirsiniz !!(self =~ /regexp/). Sen karakter sınıfını '\ d' yerine kullanabilirsiniz[0-9]
wich

1
Bir tam sayı için en basit normal ifade muhtemelen / ^ \ d + $ /
keithxm23

169

İşte kolay yol:

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

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

Dizeyi dönüştürmek için bir istisnayı tetikleyen çözümlere katılmıyorum - istisnalar akış kontrolü değildir ve siz de bunu doğru şekilde yapabilirsiniz. Bununla birlikte, yukarıdaki çözümüm 10 tabanlı olmayan tam sayılarla ilgilenmiyor. İşte istisnalara başvurmadan yapmanın yolu:

  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

27
"01" .to_i.to_s! = "01"
sepp2k

2
Değiştirmek Could self.to_i.to_s == selfile Integer self rescue false?
Meredith L. Patterson

5
Yapabilirsin, ama bu kötü bir biçim olur. İstisnaları kontrol akışı olarak kullanmazsınız ve hiç kimsenin kodu "rescue false" (veya "rescue true") içermemelidir. Bazı basit gsub'lama, çözümümün OP tarafından belirtilmeyen uç durumlar için çalışmasını sağlar.
Sarah Mei

4
Pek çok insanın bunu kullandığını biliyorum ve kesinlikle estetik açıdan hoş. Bana göre bu, kodun yeniden yapılandırılması gerektiğinin bir göstergesi. Bir istisna bekliyorsanız ... bu bir istisna değildir.
Sarah Mei

2
İstisnaların kontrol akışı olarak kullanılmaması gerektiğini kabul ediyorum. Gereksinimin geliştirici odaklı sayıların tanınması olduğunu sanmıyorum. Programcı olmayan durumlarda, özellikle önde gelen sıfırlar ve sekizli arasındaki olası kafa karışıklığı göz önüne alındığında, bir hata olarak görülebilecek. Ayrıca to_i ile tutarlı değil. Kodunuz "-0123" durumunu işlemiyor. Bu durumu hallettikten sonra, sekizli için ayrı bir normal ifadeye ihtiyacınız yoktur. "Herhangi biri" kullanarak daha da ileri gidebilirsiniz. İşlevinizdeki tek ifade "[/ re1 /, / re2 /, / re3 /] .any? {| Re | self = ~ re}" olabilir, if cümleleri veya dönüşleri yoktur.
janm

67

Kullanabilir Integer(str)ve yükseltip yükseltmediğini görebilirsiniz:

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

Bunun için doğru dönmese de "01", bunun için değil "09", çünkü 09geçerli bir tamsayı değişmez değeri olmayacağına dikkat edilmelidir. İstediğiniz davranış bu değilse, 10ikinci argüman olarak ekleyebilirsiniz Integer, böylece sayı her zaman 10 tabanı olarak yorumlanır.


39
Dostum ... sadece bir sayıyı dönüştürmek için bir istisna mı kışkırtıyor? İstisnalar, kontrol akışı değildir.
Sarah Mei

29
Değiller, ancak maalesef bu Ruby'de bir dizgenin "bütünlüğünü" belirlemenin kurallı yoludur. Kullanılan yöntemler #to_i, izin verilebilirliği nedeniyle çok bozuk.
Avdi

17
Nedenini merak edenler için Tamsayı ("09") geçerli değildir çünkü "0" onu sekizlik yapar ve 9 geçerli bir sekizlik sayı değildir. osdir.com/ml/lang.ruby.general/2002-08/msg00247.html
Andrew Grimm

20
Sarah: Bir Regex kullanabilirsiniz, ancak Ruby'nin tamsayıları (negatif sayılar, onaltılık, sekizlik, altçizgiler, örneğin 1_000_000) ayrıştırırken yaptığı tüm durumları ele almak için çok büyük bir Normal ifade olur ve yanılması kolaydır. Integer()bu kurallara uygundur çünkü Integer ()Ruby'nin tamsayı olarak gördüğü her şeyin kabul edileceğini ve diğer her şeyin reddedileceğini kesin olarak bilirsiniz. Dilin size zaten verdiklerini kopyalamak, kontrol için istisnalar kullanmaktan muhtemelen daha kötü bir kod kokusu.
Avdi

9
@Rado So tekerleği yeniden icat ediyor.
sepp2k

24

Tek bir astar yapabilirsiniz:

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

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

ya da

int = Integer(str) rescue false

Ne yapmaya çalıştığınıza bağlı olarak, doğrudan rescue cümlesiyle bir başlangıç ​​bitiş bloğu da kullanabilirsiniz:

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

  i.times do |j|
    puts j
  end
rescue ArgumentError
  puts "Not an int, doing something else"
end

1
"Kontrol akışı olarak istisna" konusuyla ilgili olarak: Elimizdeki yöntemin nasıl kullanılacağını bilmediğimizden, istisnaların uygun olup olmayacağına gerçekten karar veremeyiz. Dize bir girdi ise ve bir tamsayı olması gerekiyorsa, tamsayı olmayan bir dizinin sağlanması bir istisna anlamına gelecektir. O zaman belki de işleme aynı yöntemde olmayabilir ve muhtemelen tamsayı (str) yaparız .times {| i | i} veya her neyse koyar.
Robert Klemme

24
"12".match(/^(\d)+$/)      # true
"1.2".match(/^(\d)+$/)     # false
"dfs2".match(/^(\d)+$/)    # false
"13422".match(/^(\d)+$/)   # true

4
O dönmez trueve falseancak MatchDataörneklerini venil
Stefan

Geri döndüğü değil,
eşleşirse

5
İle sarın !!veya kullanım present?Eğer bir boolean gerekiyorsa !!( "12".match /^(\d)+$/ )veya "12".match(/^(\d)+$/).present?(ikincisi gerektiren Raylar / ActiveSupport)
mahemoff

1
Bu normal ifade bir işareti hesaba katmaz: Negatif sayılar da geçerli tam sayılardır . Şimdi geçerli doğal sayılar veya sıfır için test ediyorsunuz .
Jochem Schulenklopper


8
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. Ön eki yoktur is_. Bunu soru işareti yöntemlerinde aptalca buluyorum, "04".integer?daha çok seviyorum "foo".is_integer?.
  2. Sepp2k'in mantıklı çözümünü kullanır, bu da "01"böyle geçer .
  3. Nesne odaklı, yay.

1
Bunu # tamsayı? Olarak adlandırmak için +1, onunla dize yapmak için -1 :-P
Avdi

1
Başka nereye giderdi? integer?("a string")FTL.
Ağustos Lilleaas

2
String#integer?her Ruby kodlayıcısının ve kuzeninin dile eklemeyi sevdiği türden bir yamadır, bu da üç farklı ince uyumsuz uygulama ve beklenmedik bozulmalara sahip kod tabanlarına yol açar. Bunu büyük Ruby projelerinde zor yoldan öğrendim.
Avdi

Yukarıdaki ile aynı yorum: kontrol akışı için istisnalar kullanılmamalıdır.
Sarah Mei

Dezavantajı: Bu çözüm bir dönüşümü boşa harcıyor.
Robert Klemme

7

En iyi ve basit yol kullanmaktır 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

IntegerAyrıca iyi ama dönecektir 0içinInteger nil


Cevabınızı fark ettiğime sevindim. Aksi takdirde, "Float" a ihtiyaç duyduğumda "Tamsayı" ile gitmiş olurdum.
Jeff Zivkovic

Bu basit ama en iyi cevap! Çoğu durumda, String sınıfına gerçekten süslü yamalara ihtiyacımız yok. Bu benim için en iyisi!
Anh Nguyen

(Float (değer) kurtarma yanlış)? Float (değer) .to_s == değer? Float (değer): Tamsayı (değer): değer
okliv

6

Tercih ederim:

yapılandırma / başlatıcılar / string.rb

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

ve sonra:

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

veya telefon numarasını kontrol edin:

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

4

Çok daha basit bir yol olabilir

/(\D+)/.match('1221').nil? #=> true
/(\D+)/.match('1a221').nil? #=> false
/(\D+)/.match('01221').nil? #=> true

3
  def isint(str)
    return !!(str =~ /^[-+]?[1-9]([0-9]*)?$/)
  end

Yalnızca kod yanıtları pek kullanışlı değildir. Bunun yerine nasıl çalıştığına ve neden uygun cevap olduğuna dair bir açıklama yapın. Gelecek için eğitmek istiyoruz, böylece acil soruyu çözmek değil, çözüm anlaşılır.
The Tin Man

3

Şahsen istisna yaklaşımını seviyorum, ancak bunu biraz daha özlü yapsam da:

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

Ancak, diğerlerinin daha önce de belirttiği gibi, bu Sekizli dizelerle çalışmaz.


2

Ruby 2.4'te Regexp#match?: (a ile ?)

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

Daha eski Ruby sürümleri için Regexp#===. Durum eşitliği operatörünün doğrudan kullanımından genellikle kaçınılması gerekse de, burada çok temiz görünüyor:

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

2

Bu, aşağıdakileri kullanarak tüm durumlar için uygun olmayabilir:

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

bazıları için de yapabilir.

0 değilse ve bir sayı ise, bir sayı döndürür. 0 döndürürse, bu bir dizedir veya 0'dır.


11
Çalışıyor ama o zamandan beri tavsiye edilmiyor "12blah".to_i => 12. Bu, garip senaryolarda bazı sorunlara neden olabilir.
rfsbraz

2

İşte benim çözümüm:

# /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

Ve işte şu şekilde çalışıyor:

  • /^(\d)+$/olduğu regex herhangi dizede basamak bulmak için ifade. Normal ifade ifadelerinizi ve sonuçlarınızı http://rubular.com/ adresinde test edebilirsiniz .
  • IntegerRegexYöntemde her kullandığımızda gereksiz bellek tahsisini önlemek için sabit olarak kaydederiz.
  • integer?trueveya dönmesi gereken sorgulayıcı bir yöntemdir false.
  • matchbağımsız değişkendeki verilen normal ifade ifadesine göre oluşumlarla eşleşen ve eşleşen değerleri veya döndüren dize üzerinde bir yöntemdir nil.
  • !!matchyöntemin sonucunu eşdeğer boolean'a dönüştürür.
  • Ve mevcut Stringsınıfta yöntemin bildirilmesi, mevcut String işlevlerinde hiçbir şeyi değiştirmeyen, ancak yalnızca integer?herhangi bir String nesnesinde adlandırılmış başka bir yöntem ekleyen maymun yamasıdır .

1
Buna biraz açıklama ekleyebilir misiniz lütfen?
stef

@stef - Ben de aynısını yaptım. Hala sorunuz varsa lütfen bana bildirin.
Sachin

1

@ Rado'nun yukarıdaki cevabını genişletmek, çift patlama kullanmadan doğru veya yanlış boolelerin geri dönüşünü zorlamak için üçlü bir ifade de kullanabilir. Kabul edildiğinde, çifte mantıksal olumsuzlama versiyonu daha kısa ama muhtemelen yeni gelenler için (benim gibi) okuması daha zor.

class String
  def is_i?
     self =~ /\A[-+]?[0-9]+\z/ ? true : false
  end
end

Normal ifadeler kullanmanın Ruby'yi çok daha fazla iş yapmaya zorladığını ve bu bir döngüde kullanılırsa kodu yavaşlatacağını düşünün. İfadeyi sabitlemek yardımcı olur, ancak normal ifade yine de önemli ölçüde yavaştır.
The Tin Man

1

Daha genelleştirilmiş durumlar için (ondalık noktalı sayılar dahil) aşağıdaki yöntemi deneyebilirsiniz:

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

Bu yöntemi bir irb oturumunda test edebilirsiniz:

(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

Burada yer alan normal ifadenin ayrıntılı açıklaması için bu blog makalesine göz atın :)


Çağırmak gerekmez mi obj.is_a? Stringçünkü dize # to_s çok fazla ile karşılaştırıldığında işlem gerektirmez tahmin, hangi kendini dönecektir .is_a?çağrı. Bu şekilde, bu hatta bir veya iki yerine yalnızca bir arama yapacaksınız. Ayrıca, doğrudan yöntemin !!içine dahil edebilirsiniz number?, çünkü kural gereği, ile biten bir yöntem adının ?bir boole değeri döndürmesi gerekir. Saygılarımızla!
Giovanni Benussi

-1

Aşağıdakileri çok seviyorum:

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

Yine de dikkatli olun:

is_integer?('123sdfsdf')
=> true

-2

Bir astar string.rb

def is_integer?; true if Integer(self) rescue false end

-3

Bu soru sorulduğunda bunun ortalıkta olup olmadığından emin değilim, ancak bu gönderiye rastlayan herkes için en basit yol:

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? herhangi bir nesne ile çalışacaktır.


Asıl sorunun sorduğu bu değil. OP, dizenin bir tam sayı olup olmadığını da bilmek ister. örneğin "12".is_an_integer? == true "not12".is_an_integer? == false 12.is_an_integer? == true
Marklar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.