Cách kiểm tra nếu một chuỗi về cơ bản là một số nguyên trong dấu ngoặc kép bằng Ruby


128

Tôi cần một chức năng is_an_integer, ở đâu

  • "12".is_an_integer? trả về đúng
  • "blah".is_an_integer? trả về sai.

Làm thế nào tôi có thể làm điều này trong Ruby? Tôi sẽ viết một regex nhưng tôi cho rằng có một người trợ giúp cho việc này mà tôi không biết.



1
Hãy cẩn thận sử dụng các giải pháp dựa trên các biểu thức thông thường. Điểm chuẩn cho thấy họ chạy chậm hơn nhiều so với mã thông thường.
Người đàn ông Tin

Câu trả lời:


135

Bạn có thể sử dụng các biểu thức thông thường. Đây là chức năng với các đề xuất của @ janm.

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

Một phiên bản chỉnh sửa theo nhận xét từ @wich:

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

Trong trường hợp bạn chỉ cần kiểm tra số dương

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

4
Không tệ. Trong Ruby, bạn thường bỏ qua từ khóa "return" nếu giá trị trả về được tạo trong biểu thức cuối cùng trong hàm. Điều này cũng sẽ trả về một giá trị số nguyên bằng 0, bạn có thể muốn một boolean, vì vậy một cái gì đó như !! (str = ~ / ^ [- +]? [0-9] + $ /) sẽ làm điều đó. Sau đó, bạn có thể thêm nó vào Chuỗi và bỏ qua đối số, sử dụng "self" thay vì "str", và sau đó bạn có thể thay đổi tên thành "is_i?" ...
janm

2
Cảm ơn! Tôi hoàn toàn không có manh mối về các quy ước và thực hành ruby. Tôi vừa làm một google nhanh về ruby ​​và các biểu thức thông thường để xem cú pháp, thay đổi biểu thức chính quy để áp dụng cho vấn đề trong tay và kiểm tra nó. Nó thực sự khá gọn gàng .. Tôi có thể phải cho nó một cái nhìn dài hơn khi tôi có nhiều thời gian rảnh rỗi hơn.
Rado

Bạn đã có ý tưởng đúng, nhưng nó không khớp với các chữ nhị phân hoặc hex (xem giải pháp chỉnh sửa của tôi bên dưới).
Sarah Mei

16
Hai bình luận. Bạn có thể sử dụng /regexp/ === selfthay vì !!(self =~ /regexp/)xây dựng. Bạn có thể sử dụng lớp nhân vật '\ d' thay vì[0-9]

1
Regex đơn giản nhất cho một số nguyên có lẽ là / ^ \ d + $ /
keithxm23

169

Chà, đây là cách dễ dàng:

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

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

Tôi không đồng ý với các giải pháp kích thích ngoại lệ để chuyển đổi chuỗi - ngoại lệ không kiểm soát luồng và bạn cũng có thể thực hiện đúng cách. Điều đó nói rằng, giải pháp của tôi ở trên không đối phó với các số nguyên không phải là số 10. Vì vậy, đây là cách để làm mà không cần dùng đến ngoại lệ:

  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
Bạn không thể thay thế self.to_i.to_s == selfbằng Integer self rescue false?
Meredith L. Patterson

5
Bạn có thể, nhưng đó sẽ là hình thức xấu. Bạn không sử dụng ngoại lệ làm luồng kiểm soát và không bao giờ mã của ai đó chứa "giải cứu sai" (hoặc "giải cứu đúng"). Một số gsub'ing đơn giản sẽ làm cho giải pháp của tôi hoạt động cho các trường hợp cạnh không được chỉ định bởi OP.
Sarah Mei

4
Tôi biết rất nhiều người sử dụng nó, và nó chắc chắn là về mặt thẩm mỹ. Đối với tôi mặc dù đó là một dấu hiệu cho thấy mã cần tái cấu trúc. Nếu bạn đang mong đợi một ngoại lệ ... thì đó không phải là ngoại lệ.
Sarah Mei

2
Tôi đồng ý rằng các ngoại lệ không nên được sử dụng như luồng điều khiển. Tôi không nghĩ rằng yêu cầu là số lượng nhà phát triển được công nhận. Trong các tình huống không phải lập trình viên có thể được coi là một lỗi, đặc biệt là có thể có sự nhầm lẫn có thể xảy ra xung quanh các số 0 và bát phân hàng đầu. Cũng không phù hợp với to_i. Mã của bạn không xử lý trường hợp "-0123". Khi bạn xử lý trường hợp đó, bạn không cần một biểu thức chính quy cho bát phân. Bạn có thể đơn giản hơn nữa bằng cách sử dụng "bất kỳ?". Câu lệnh duy nhất trong hàm của bạn có thể là "[/ re1 /, / re2 /, / re3 /] .any? {| Re | self = ~ re}", không có mệnh đề hoặc trả về.
janm

67

Bạn có thể sử dụng Integer(str)và xem nếu nó tăng:

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

Cần phải chỉ ra rằng trong khi điều này không trả về đúng "01", thì nó không đúng "09", đơn giản vì 09nó sẽ không phải là một số nguyên hợp lệ. Nếu đó không phải là hành vi bạn muốn, bạn có thể thêm 10làm đối số thứ hai Integer, vì vậy số này luôn được hiểu là cơ sở 10.


39
Anh bạn ... kích động một ngoại lệ chỉ để chuyển đổi một số? Ngoại lệ không kiểm soát dòng chảy.
Sarah Mei

29
Họ không, nhưng thật không may, đây là cách chính tắc để xác định "tính toàn vẹn" của chuỗi trong Ruby. Các phương pháp sử dụng #to_iquá hỏng vì tính dễ dãi của nó.
Avdi

17
Đối với những người thắc mắc tại sao, Số nguyên ("09") không hợp lệ vì "0" làm cho số bát phân và 9 không phải là số bát phân hợp lệ. osdir.com/ml/lang.ruby.general/2002-08/msg00247.html
Andrew Grimm

20
Sarah: bạn có thể sử dụng Regex nhưng để xử lý tất cả các trường hợp mà Ruby thực hiện khi phân tích số nguyên (số âm, hex, bát phân, dấu gạch dưới, ví dụ 1_000_000), đó sẽ là một Regex rất lớn và dễ bị sai. Integer()là hợp quy vì với Integer ()bạn biết chắc chắn rằng bất cứ điều gì mà Ruby coi là một số nguyên sẽ được chấp nhận và mọi thứ khác sẽ bị từ chối. Sao chép những gì ngôn ngữ đã cung cấp cho bạn có thể là mùi mã tệ hơn so với việc sử dụng ngoại lệ để kiểm soát.
Avdi

9
@Rado Vì vậy, đang phát minh lại bánh xe.
sepp2k

24

Bạn có thể làm một lớp lót:

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

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

hoặc thậm chí

int = Integer(str) rescue false

Tùy thuộc vào những gì bạn đang cố gắng làm, bạn cũng có thể trực tiếp sử dụng khối kết thúc bắt đầu với điều khoản giải cứu:

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

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

1
Liên quan đến chủ đề "ngoại lệ như luồng điều khiển": vì chúng tôi không biết phương pháp trong tay được sử dụng như thế nào, chúng tôi không thể thực sự đánh giá liệu ngoại lệ có phù hợp hay không. Nếu chuỗi là đầu vào và nó được yêu cầu là một số nguyên, thì việc cung cấp một số nguyên không sẽ đảm bảo một ngoại lệ. Mặc dù sau đó có thể việc xử lý không theo cùng một phương thức và có lẽ chúng ta sẽ chỉ thực hiện Integer (str) .times {| i | đặt i} hoặc bất cứ điều gì.
Robert Klemme

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

4
Nó không trở lại truefalsenhưng các MatchDatatrường hợp vànil
Stefan

Nó không phải là những gì nó trả lại, nhưng nếu nó phù hợp
Maciej Krasnowski

5
Gói lại !!hoặc sử dụng present?nếu bạn cần boolean !!( "12".match /^(\d)+$/ )hoặc "12".match(/^(\d)+$/).present?(cái sau cần Rails / activesupport)
mahemoff

1
Biểu thức chính quy này không đưa vào tài khoản: số âm cũng là số nguyên hợp lệ . Bây giờ bạn đang kiểm tra các số tự nhiên hợp lệ hoặc bằng không.
Joool Schulenklopper


8
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. Nó không có tiền tố is_. Tôi thấy rằng ngớ ngẩn trên các phương pháp dấu hỏi, tôi thích "04".integer?hơn rất nhiều "foo".is_integer?.
  2. Nó sử dụng giải pháp hợp lý bởi sepp2k, vượt qua "01"và như vậy.
  3. Hướng đối tượng, yay.

1
+1 để đặt tên cho nó #integer?, -1 để làm lộn xộn Chuỗi với nó :-P
Avdi

1
Nó sẽ đi đâu nữa? integer?("a string")ftl.
Tháng 8 Lilleaas

2
String#integer?là loại bản vá phổ biến mà mọi lập trình viên Ruby và anh em họ của họ muốn thêm vào ngôn ngữ, dẫn đến các cơ sở mã với ba triển khai không tương thích tinh tế khác nhau và phá vỡ bất ngờ. Tôi đã học được điều này một cách khó khăn trong các dự án lớn của Ruby.
Avdi

Nhận xét tương tự như trên: ngoại lệ không nên được sử dụng cho luồng điều khiển.
Sarah Mei

Nhược điểm: giải pháp này đang lãng phí một chuyển đổi.
Robert Klemme

7

Cách tốt nhất và đơn giản là sử dụng 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

Integercũng tốt nhưng nó sẽ trở lại 0choInteger nil


Tôi rất vui vì tôi nhận thấy câu trả lời của bạn. Nếu không thì tôi đã đi với "Integer" khi tôi cần "Float".
Jeff Zivkovic

Đây là đơn giản nhưng câu trả lời tốt nhất! Chúng tôi thực sự không cần bản vá ưa thích cho lớp String trong hầu hết các trường hợp. Điều này làm việc tốt nhất cho tôi!
Anh Nguyễn

(Phao (giá trị) giải cứu sai)? Float (giá trị) .to_s == giá trị? Float (value): Integer (value): value
okliv

6

Tôi thích:

cấu hình / khởi tạo / string.rb

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

và sau đó:

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

hoặc kiểm tra số điện thoại:

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

4

Một cách đơn giản hơn nhiều có thể là

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

Mã chỉ trả lời không hữu ích lắm. Thay vào đó cung cấp một lời giải thích về cách thức hoạt động và tại sao đó là câu trả lời thích hợp. Chúng tôi muốn giáo dục cho tương lai để giải pháp được hiểu, không giải quyết được câu hỏi ngay lập tức.
Người đàn ông Tin

3

Cá nhân tôi thích cách tiếp cận ngoại lệ mặc dù tôi sẽ làm cho nó ngắn gọn hơn một chút:

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

Tuy nhiên, như những người khác đã nêu, điều này không hoạt động với các chuỗi Octal.


2

Ruby 2.4 có Regexp#match?: (với a ?)

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

Đối với các phiên bản Ruby cũ hơn, có Regexp#===. Và mặc dù thường nên tránh sử dụng trực tiếp toán tử đẳng thức, nhưng có vẻ rất rõ ràng ở đây:

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

Điều này có thể không phù hợp cho tất cả các trường hợp sử dụng đơn giản:

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

cũng có thể làm cho một số.

Nếu đó là một số và không phải 0, nó sẽ trả về một số. Nếu nó trả về 0 thì đó là một chuỗi hoặc 0.


11
Hoạt động nhưng nó không được khuyến khích, kể từ đó "12blah".to_i => 12. Điều này có thể gây ra một số rắc rối trong các kịch bản kỳ lạ.
rfsbraz

2

Đây là giải pháp của tôi:

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

Và đây là cách nó hoạt động:

  • /^(\d)+$/là biểu thức regex để tìm các chữ số trong bất kỳ chuỗi nào. Bạn có thể kiểm tra biểu thức và kết quả regex của mình tại http://rubular.com/ .
  • Chúng tôi lưu nó trong một hằng số IntegerRegexđể tránh phân bổ bộ nhớ không cần thiết mỗi khi chúng tôi sử dụng nó trong phương thức.
  • integer?là một phương pháp hỏi cung nên trả lại truehoặc false.
  • matchlà một phương thức trên chuỗi khớp với các lần xuất hiện theo biểu thức regex đã cho trong đối số và trả về các giá trị khớp hoặc nil.
  • !!chuyển đổi kết quả của matchphương thức thành boolean tương đương.
  • Và khai báo phương thức trong Stringlớp hiện tại là vá khỉ, nó không thay đổi bất cứ điều gì trong các chức năng Chuỗi hiện có, mà chỉ thêm một phương thức khác có tên integer?trên bất kỳ đối tượng Chuỗi nào.

1
Bạn có thể thêm một chút giải thích cho điều này xin vui lòng?
stef

@stef - Tôi cũng đã làm như vậy. Xin vui lòng cho tôi biết nếu bạn có bất kỳ câu hỏi vẫn còn.
Sachin

1

Mở rộng câu trả lời của @ rado ở trên, người ta cũng có thể sử dụng một câu lệnh tạm thời để buộc trả lại các booleans đúng hoặc sai mà không cần sử dụng tiếng nổ kép. Cấp, phiên bản phủ định logic kép ngắn gọn hơn, nhưng có lẽ khó đọc hơn cho người mới (như tôi).

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

Hãy xem xét rằng việc sử dụng các biểu thức thông thường buộc Ruby phải thực hiện nhiều công việc hơn vì vậy nếu điều này được sử dụng trong một vòng lặp, nó sẽ làm chậm mã. Neo biểu thức giúp, nhưng regex vẫn chậm hơn đáng kể.
Người đàn ông Tin

1

Đối với các trường hợp tổng quát hơn (bao gồm các số có dấu thập phân), bạn có thể thử phương pháp sau:

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

Bạn có thể kiểm tra phương pháp này trong phiên 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

Để được giải thích chi tiết về regex liên quan ở đây, hãy xem bài viết blog này :)


Không cần thiết phải gọi obj.is_a? StringChuỗi # to_s sẽ tự trả về, mà tôi đoán không yêu cầu xử lý quá nhiều so với .is_a?cuộc gọi. Bằng cách này, bạn sẽ chỉ thực hiện một cuộc gọi trong đường dây này thay vì một hoặc hai cuộc gọi. Ngoài ra, bạn có thể bao gồm trực tiếp !!bên trong number?phương thức, bởi vì theo quy ước, một tên phương thức kết thúc bằng ?được cho là trả về giá trị boolean. Trân trọng!
Giovanni Benussi

-1

Tôi thích như sau, thẳng tiến:

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

Mặc dù cẩn thận:

is_integer?('123sdfsdf')
=> true

-2

Một lớp lót trong string.rb

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

-3

Tôi không chắc liệu đây có phải là khi câu hỏi này được hỏi hay không nhưng với bất kỳ ai tình cờ thấy bài đăng này, cách đơn giản nhất là:

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? sẽ làm việc với bất kỳ đối tượng.


Đó không phải là những gì câu hỏi ban đầu đang hỏi. OP muốn biết nếu chuỗi cũng là một số nguyên. ví dụ "12".is_an_integer? == true "not12".is_an_integer? == false 12.is_an_integer? == true
Marklar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.