วิธีการทดสอบว่าสตริงนั้นเป็นจำนวนเต็มในเครื่องหมายคำพูดโดยใช้ Ruby หรือไม่


128

ฉันต้องการฟังก์ชั่นis_an_integerที่ไหน

  • "12".is_an_integer? ผลตอบแทนจริง
  • "blah".is_an_integer? ผลตอบแทนที่เป็นเท็จ

ฉันจะทำสิ่งนี้ในรูบีได้อย่างไร ฉันจะเขียน regex แต่ฉันสมมติว่ามีผู้ช่วยสำหรับสิ่งนี้ที่ฉันไม่ทราบ



1
ระมัดระวังการใช้โซลูชันที่ใช้นิพจน์ทั่วไป มาตรฐานแสดงให้เห็นว่าพวกเขาทำงานช้ากว่ารหัสปกติมาก
ชายดีบุก

คำตอบ:


135

คุณสามารถใช้นิพจน์ทั่วไป นี่คือฟังก์ชั่นที่มีคำแนะนำของ @ janm

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

รุ่นที่แก้ไขตามความคิดเห็นจาก @wich:

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

ในกรณีที่คุณต้องตรวจสอบตัวเลขบวก

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

4
ไม่เลว. ในทับทิมคุณมักจะละเว้นคำหลัก "คืน" หากค่าตอบแทนถูกสร้างขึ้นในการแสดงออกครั้งสุดท้ายในฟังก์ชั่น สิ่งนี้จะส่งกลับค่าจำนวนเต็มศูนย์คุณอาจต้องการบูลีนดังนั้นอย่างเช่น !! (str = ~ / ^ [- +]? [0-9] + $ /) จะทำเช่นนั้น จากนั้นคุณสามารถเพิ่มลงใน String และเลิกใช้อาร์กิวเมนต์โดยใช้ "ตัวเอง" แทน "str" ​​จากนั้นคุณสามารถเปลี่ยนชื่อเป็น "is_i" ...
janm

2
ขอบคุณ! ฉันไม่มีข้อสงสัยเกี่ยวกับการประชุมและการปฏิบัติทับทิม ฉันเพิ่งทำ google อย่างรวดเร็วบน ruby ​​และนิพจน์ทั่วไปเพื่อดูไวยากรณ์เปลี่ยน regex เพื่อนำไปใช้กับปัญหาที่เกิดขึ้นและทดสอบ มันค่อนข้างเรียบร้อยจริง ๆ .. ฉันอาจต้องดูอีกต่อไปเมื่อฉันมีเวลามากขึ้น
Rado

คุณมีความคิดที่ถูกต้อง แต่ไม่ตรงกับตัวอักษรไบนารีหรือฐานสิบหก (ดูโซลูชันที่แก้ไขด้านล่างของฉัน)
Sarah Mei

16
สองความคิดเห็น คุณสามารถใช้/regexp/ === selfแทน!!(self =~ /regexp/)โครงสร้าง คุณสามารถใช้คลาสของตัวละคร '\ d' แทน[0-9]

1
regex ที่ง่ายที่สุดสำหรับจำนวนเต็มอาจเป็น / ^ \ d + $ /
keithxm23

169

นี่เป็นวิธีที่ง่าย:

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

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

ฉันไม่เห็นด้วยกับวิธีแก้ปัญหาที่กระตุ้นให้เกิดข้อยกเว้นในการแปลงสตริง - ข้อยกเว้นไม่ใช่การควบคุมการไหลและคุณอาจทำในสิ่งที่ถูกต้อง ที่กล่าวว่าวิธีการแก้ปัญหาของฉันไม่ได้จัดการกับจำนวนเต็มไม่ใช่ฐาน 10 ดังนั้นนี่คือวิธีการทำโดยไม่ต้องใช้ข้อยกเว้น:

  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
คุณไม่สามารถแทนที่self.to_i.to_s == selfด้วยInteger self rescue false?
Meredith L. Patterson

5
คุณทำได้ แต่นั่นจะเป็นรูปแบบที่ไม่ดี คุณไม่ได้ใช้ข้อยกเว้นเป็นโฟลว์ควบคุมและไม่ควรมีรหัสของใครเลยที่มี "rescue false" (หรือ "rescue true") gsub'ing ง่าย ๆ บางอย่างจะทำให้โซลูชันของฉันทำงานกับเคสขอบที่ไม่ได้ระบุโดย OP
Sarah Mei

4
ฉันรู้ว่าผู้คนจำนวนมากใช้มันและมันก็เป็นที่ชื่นชอบอย่างแน่นอน ถึงฉันมันเป็นข้อบ่งชี้ว่ารหัสต้องการการปรับโครงสร้าง หากคุณคาดหวังว่าจะมีข้อยกเว้น ... ไม่ใช่ข้อยกเว้น
Sarah Mei

2
ฉันยอมรับว่าข้อยกเว้นไม่ควรใช้เป็นโฟลว์ควบคุม ฉันไม่คิดว่าข้อกำหนดคือหมายเลขที่มุ่งเน้นนักพัฒนาได้รับการยอมรับ ในสถานการณ์ที่ไม่ใช่โปรแกรมเมอร์ซึ่งอาจถูกมองว่าเป็นจุดบกพร่องโดยเฉพาะอย่างยิ่งเนื่องจากความสับสนที่อาจเกิดขึ้นกับศูนย์นำหน้าและเลขฐานแปด ยังไม่สอดคล้องกับ to_i รหัสของคุณไม่ได้รองรับกรณี "-0123" เมื่อคุณจัดการกับกรณีนั้นแล้วคุณไม่จำเป็นต้องใช้ regexp แยกสำหรับเลขฐานแปด คุณสามารถเพิ่มเติมโดยใช้ "ใด ๆ ?" คำสั่งเดียวในฟังก์ชันของคุณอาจเป็น "[/ re1 /, / re2 /, / re3 /] .any? {| re | self = ~ re}" โดยไม่มีถ้า clause หรือส่งคืน
janm

67

คุณสามารถใช้Integer(str)และดูว่ามันเพิ่ม:

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

มันควรจะชี้ให้เห็นว่าในขณะที่สิ่งนี้กลับเป็นจริงสำหรับ"01"มันไม่ได้"09"เพราะเพียงเพราะ09จะไม่เป็นตัวอักษรที่ถูกต้องจำนวนเต็ม หากนั่นไม่ใช่พฤติกรรมที่คุณต้องการคุณสามารถเพิ่ม10เป็นอาร์กิวเมนต์ที่สองIntegerได้ดังนั้นตัวเลขจะถูกตีความว่าเป็นฐาน 10 เสมอ


39
เพื่อน ... ยั่วข้อยกเว้นเพียงเพื่อแปลงตัวเลขหรือไม่ ข้อยกเว้นไม่ได้ควบคุมการไหล
Sarah Mei

29
พวกเขาไม่ได้ แต่น่าเสียดายที่นี่เป็นวิธีที่บัญญัติเพื่อกำหนด "จำนวนเต็ม" ของสตริงในทับทิม วิธีการใช้งาน#to_iนั้นเสียเกินไปเพราะความยินยอม
Avdi

17
สำหรับผู้ที่สงสัยว่าทำไม Integer ("09") ไม่ถูกต้องเพราะ "0" ทำให้เป็นฐานแปดและ 9 ไม่ใช่หมายเลขฐานแปดที่ถูกต้อง osdir.com/ml/lang.ruby.general/2002-08/msg00247.html
Andrew Grimm

20
Sarah: คุณสามารถใช้ Regex ได้ แต่เพื่อจัดการกับทุกกรณีที่ Ruby ทำเมื่อทำการแจงจำนวนเต็ม (จำนวนลบ, ฐานสิบหก, ฐานแปด, ขีดล่างเช่น 1_000_000) มันจะเป็น Regex ที่ใหญ่และง่ายต่อการเข้าใจผิด Integer()เป็นที่ยอมรับเพราะInteger ()คุณรู้ว่าสิ่งใดก็ตามที่ Ruby พิจารณาว่าเป็นตัวอักษรจำนวนเต็มจะได้รับการยอมรับและทุกอย่างอื่นจะถูกปฏิเสธ การทำซ้ำสิ่งที่ภาษาให้คุณนั้นมีกลิ่นรหัสที่แย่กว่าการใช้ข้อยกเว้นสำหรับการควบคุม
Avdi

9
@Rado ดังนั้นการคิดค้นล้อใหม่
sepp2k

24

คุณสามารถทำหนึ่งซับ:

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

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

หรือแม้กระทั่ง

int = Integer(str) rescue false

ขึ้นอยู่กับสิ่งที่คุณพยายามทำคุณสามารถใช้บล็อกจุดเริ่มต้นโดยตรงกับส่วนคำสั่งช่วยเหลือ:

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

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

1
ในส่วนที่เกี่ยวกับหัวข้อ "exception as control flow": เนื่องจากเราไม่ทราบว่าจะใช้วิธีการในมือได้อย่างไรเราจึงไม่สามารถตัดสินได้ว่าข้อยกเว้นจะเหมาะสมหรือไม่ หากสตริงเป็นอินพุตและจำเป็นต้องมีจำนวนเต็มดังนั้นการระบุว่าไม่ใช่จำนวนเต็มจะรับประกันข้อยกเว้น ถึงแม้ว่าบางทีการจัดการอาจไม่ได้อยู่ในวิธีการเดียวกันและเราอาจทำแค่จำนวนเต็ม (str) .times {| i | ทำให้ i} หรืออะไรก็ตาม
โรเบิร์ต Klemme

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

4
มันไม่ได้กลับมาtrueและfalseแต่MatchDataกรณีและnil
สเตฟาน

มันไม่ใช่สิ่งที่มันจะกลับมา แต่ถ้ามันตรงกัน
Maciej Krasowski

5
ห่อด้วย!!หรือใช้present?ถ้าคุณต้องการบูลีน!!( "12".match /^(\d)+$/ )หรือ"12".match(/^(\d)+$/).present?(หลังต้องการ Rails / แอคทีฟซัพพอร์ท)
mahemoff

1
นิพจน์ทั่วไปนี้ไม่คำนึงถึงการลงชื่อเข้าใช้: จำนวนลบเป็นจำนวนเต็มที่ถูกต้องเช่นกัน ตอนนี้คุณกำลังทดสอบตัวเลขหรือศูนย์ที่ถูกต้อง
Jochem Schulenklopper

13

Ruby 2.6.0 เปิดใช้งานการแคสต์เป็นจำนวนเต็มโดยไม่เพิ่มข้อยกเว้นและจะส่งคืนnilหากการแคสต์ล้มเหลว และเนื่องจากnilพฤติกรรมส่วนใหญ่falseใน Ruby คุณสามารถตรวจสอบจำนวนเต็มดังนี้:

if Integer(my_var, exception: false)
  # do something if my_var can be cast to an integer
end

8
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. มันไม่ได้is_ขึ้นต้นด้วย ผมพบว่าวิธีการโง่ questionmark ผมชอบมากดีกว่า"04".integer?"foo".is_integer?
  2. มันใช้วิธีแก้ปัญหาที่สมเหตุสมผลโดย sepp2k ซึ่งผ่านไป"01"และเช่นนั้น
  3. เชิงวัตถุใช่

1
+1 สำหรับการตั้งชื่อมัน #integer?, -1 สำหรับความยุ่งเหยิงของ String ด้วย :-P
Avdi

1
จะไปที่ไหนอีก integer?("a string")FTL
สิงหาคม Lilleaas

2
String#integer?เป็นชนิดของโปรแกรมแก้ไขทั่วไปที่ Ruby coder ทุกคนและลูกพี่ลูกน้องของพวกเขาชอบที่จะเพิ่มเข้าไปในภาษานำไปสู่ ​​codebases ที่มีการใช้งานที่ไม่เข้ากันสามอย่างที่แตกต่างกันและการแตกที่ไม่คาดคิด ฉันเรียนรู้วิธีนี้อย่างหนักในโครงการทับทิมขนาดใหญ่
Avdi

ความคิดเห็นแบบเดียวกันกับข้างบน: ไม่ควรใช้ข้อยกเว้นสำหรับโฟลว์การควบคุม
Sarah Mei

ข้อเสีย: โซลูชันนี้เสียการแปลงเพียงครั้งเดียว
Robert Klemme

7

วิธีที่ดีที่สุดและเรียบง่ายคือการใช้ 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

Integerยังดี แต่มันจะกลับมา0สำหรับInteger nil


ฉันดีใจที่ฉันสังเกตเห็นคำตอบของคุณ มิฉะนั้นฉันจะได้ไปกับ "จำนวนเต็ม" เมื่อฉันต้องการ "ลอย"
Jeff Zivkovic

นี่ง่าย แต่คำตอบที่ดีที่สุด! เราไม่ต้องการโปรแกรมปะแก้แฟนซีในคลาส String ในกรณีส่วนใหญ่ มันใช้งานได้ดีที่สุดสำหรับฉัน!
Anh Nguyen

(Float (value) rescue false)? Float (value) .to_s == value? Float (ค่า): จำนวนเต็ม (ค่า): value
okliv

6

ฉันชอบ:

config / initializers / string.rb

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

แล้ว:

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

หรือตรวจสอบหมายเลขโทรศัพท์:

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

4

วิธีที่ง่ายกว่านั้นก็คือ

/(\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

คำตอบรหัสเท่านั้นไม่มีประโยชน์มาก แทนที่จะให้คำอธิบายว่ามันทำงานอย่างไรและทำไมจึงเป็นคำตอบที่เหมาะสม เราต้องการให้ความรู้ในอนาคตเพื่อให้เข้าใจถึงวิธีการแก้ปัญหาไม่ใช่แก้ปัญหาเฉพาะหน้า
ชายดีบุก

3

โดยส่วนตัวฉันชอบวิธีการยกเว้นแม้ว่าฉันจะทำให้มันสั้นกว่า:

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

อย่างไรก็ตามอย่างที่คนอื่น ๆ ได้บอกไปแล้วมันไม่ได้ผลกับสตริงของ Octal


2

Ruby 2.4 มีRegexp#match?: (พร้อม?)

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

Regexp#===สำหรับรุ่นเก่าทับทิมมี และถึงแม้ว่าโดยทั่วไปควรหลีกเลี่ยงการใช้ตัวดำเนินการความเท่าเทียมกันของเคสโดยตรง แต่ก็ดูสะอาดตามากที่นี่:

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

สิ่งนี้อาจไม่เหมาะกับทุกกรณีโดยใช้อย่างง่าย:

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

อาจทำเพื่อบางคน

ถ้าเป็นตัวเลขไม่ใช่ 0 มันจะส่งคืนตัวเลข ถ้ามันคืนค่า 0 มันจะเป็นสตริงหรือ 0


11
ธิการ "12blah".to_i => 12แต่ก็ไม่แนะนำให้ใช้ตั้งแต่ นี่อาจทำให้เกิดปัญหาในสถานการณ์แปลก ๆ
rfsbraz

2

นี่คือทางออกของฉัน:

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

และนี่คือวิธีการ:

  • /^(\d)+$/คือการแสดงออกregexสำหรับการค้นหาตัวเลขในสตริงใด ๆ คุณสามารถทดสอบการแสดงออก regex ของคุณและผลที่http://rubular.com/
  • เราบันทึกไว้ในค่าคงที่IntegerRegexเพื่อหลีกเลี่ยงการจัดสรรหน่วยความจำที่ไม่จำเป็นทุกครั้งที่เราใช้ในวิธีการ
  • integer?เป็นวิธีที่มีคำถามที่ควรจะกลับหรือtruefalse
  • matchเป็นวิธีการในสตริงซึ่งตรงกับเหตุการณ์ที่เกิดขึ้นตามการแสดงออก regex nilได้รับในการโต้แย้งและกลับค่าที่ตรงกันหรือ
  • !!แปลงผลลัพธ์ของmatchเมธอดเป็นบูลีนที่เทียบเท่า
  • และการประกาศเมธอดในStringคลาสที่มีอยู่คือการปะของลิงซึ่งไม่ได้เปลี่ยนแปลงอะไรในฟังก์ชันของสตริงที่มีอยู่ แต่เพียงแค่เพิ่มเมธอดอื่นที่ชื่อinteger?บนวัตถุ String ใด ๆ

1
คุณช่วยเพิ่มคำอธิบายเล็กน้อยลงในสิ่งนี้ได้ไหม?
2560

@stef - ฉันทำแบบเดียวกัน โปรดแจ้งให้เราทราบหากคุณมีข้อสงสัยใด ๆ
Sachin

1

การเพิ่มคำตอบของ @ rado เหนือคำตอบหนึ่งสามารถใช้คำสั่งที่ประกอบไปด้วยเพื่อบังคับให้กลับมาเป็นบูลีนจริงหรือเท็จโดยไม่ต้องใช้คู่เรียบ จริงอยู่ที่เวอร์ชันการปฏิเสธแบบลอจิกแบบคู่นั้นมีความหมายสั้นกว่า แต่อาจอ่านได้ยากกว่าสำหรับผู้มาใหม่ (เช่นฉัน)

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

พิจารณาว่าการใช้นิพจน์ทั่วไปบังคับให้ Ruby ทำงานมากขึ้นดังนั้นหากใช้ในลูปจะทำให้โค้ดช้า การยึดการแสดงออกช่วย แต่ regex ยังช้ากว่าอย่างมาก
มนุษย์ดีบุก

1

สำหรับกรณีทั่วไปเพิ่มเติม (รวมถึงตัวเลขที่มีจุดทศนิยม) คุณสามารถลองวิธีการต่อไปนี้:

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

คุณสามารถทดสอบวิธีนี้ในเซสชั่น irb:

(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

สำหรับคำอธิบายโดยละเอียดของ regex ที่เกี่ยวข้องที่นี่ลองดูบทความในบล็อกนี้ :)


ไม่จำเป็นต้องโทรobj.is_a? StringเพราะString # to_sจะส่งคืนเองซึ่งฉันเดาว่าไม่ต้องการการประมวลผลมากเกินไปเมื่อเทียบกับการ.is_a?โทร ด้วยวิธีนี้คุณจะโทรเพียงครั้งเดียวในสายนี้แทนที่จะเป็นหนึ่งหรือสองสาย นอกจากนี้คุณสามารถรวมโดยตรง!!ภายในnumber?วิธีการเพราะโดยการประชุมชื่อวิธีการที่ลงท้ายด้วย?ควรจะส่งกลับค่าบูลีน ความนับถือ!
Giovanni Benussi

-1

ฉันชอบสิ่งต่อไปนี้ตรงไปตรงมา:

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

ระวังให้ดี:

is_integer?('123sdfsdf')
=> true


-3

ฉันไม่แน่ใจว่าสิ่งนี้เกิดขึ้นเมื่อมีการถามคำถามหรือไม่ แต่สำหรับทุกคนที่สะดุดในโพสต์นี้วิธีที่ง่ายที่สุดคือ:

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? จะทำงานกับวัตถุใด ๆ


นั่นไม่ใช่สิ่งที่คำถามเดิมถาม OP ต้องการทราบว่าสตริงเป็นจำนวนเต็มด้วยหรือไม่ เช่น "12".is_an_integer? == true "not12".is_an_integer? == false 12.is_an_integer? == true
Marklar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.