如何使用Ruby测试字符串是否基本上是带引号的整数


128

我需要一个函数,is_an_integer其中

  • "12".is_an_integer? 返回true。
  • "blah".is_an_integer? 返回false。

如何在Ruby中执行此操作?我会写一个正则表达式,但我假设有一个我不知道的帮助程序。



1
使用依赖于正则表达式的解决方案时要小心。基准测试表明,它们的运行速度比常规代码慢得多。
锡人

Answers:


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
不错。在Ruby中,如果返回值是在函数的最后一个表达式中生成的,则通常会省略“ return”关键字。这也将返回整数值零,您可能需要一个布尔值,所以像!!(str =〜/ ^ [-+]?[0-9] + $ /)这样的操作就可以做到。然后,您可以将其添加到String并使用“ self”而不是“ str”忽略参数,然后可以将名称更改为“ is_i?”。...
2009年

2
谢谢!我对Ruby的惯例和惯例一无所知。我只是在ruby和正则表达式上做了一个快速的google来查看语法,更改了正则表达式以适用于手头的问题,并对其进行了测试。实际上,它很整洁..当我有更多的空闲时间时,我可能不得不给它更长的外观。
拉多

您有正确的主意,但它与二进制或十六进制文字不匹配(请参见下面我编辑过的解决方案)。
莎拉·梅

16
两个评论。您可以使用/regexp/ === self而不是!!(self =~ /regexp/)构造。你可以用它代替字符类“\ d”[0-9]
至极

1
整数最简单的正则表达式可能是/ ^ \ 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 == selfInteger self rescue false吗?
Meredith L. Patterson,2009年

5
您可以,但是那将是不好的形式。您不要将异常用作控制流,并且任何人的代码都不应包含“ rescue false”(或“ rescue true”)。一些简单的gsubing处理将使我的解决方案适用于OP未指定的极端情况。
莎拉·梅

4
我知道很多人都在使用它,并且在美学上当然令人愉悦。对我来说,这表明代码需要重组。如果您期待一个例外...那也不例外。
莎拉·梅

2
我同意不应将异常用作控制流。我不认为要求承认面向开发人员的人数。在非程序员情况下,这可能会被视为错误,尤其是考虑到前导零和八进制可能引起混淆。也与to_i不一致。您的代码无法处理“ -0123”情况。处理完这种情况后,就不需要为八进制使用单独的正则表达式了。您可以简单地通过使用“任何?”来进一步。函数中唯一的语句可能是“ [/ re1 /,/ re2 /,/ re3 /] .any?{| re | self =〜re}”,没有if子句或返回值。
2009年

67

您可以使用Integer(str)并查看它是否引发:

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

应当指出的是,尽管这确实为true返回true "01",但对于却没有返回true "09",仅仅是因为它不是09有效的整数文字。如果这不是您想要的行为,则可以将其添加10为的第二个参数Integer,因此该数字始终被解释为以10为底的数字。


39
杜德(Dude)...挑出一个例外只是为了转换数字?异常不是控制流。
莎拉·梅

29
它们不是,但是不幸的是,这是确定Ruby中字符串“整数”的规范方法。#to_i由于使用方法过于宽泛,因此使用起来太麻烦了。
09年

17
对于那些想知道为什么的人,Integer(“ 09”)无效,因为“ 0”使其为八进制,而9不是有效的八进制数。osdir.com/ml/lang.ruby.general/2002-08/msg00247.html
Andrew Grimm,2009年

20
莎拉:您可以使用Regex,但是为了处理Ruby在解析整数(负数,十六进制,八进制,下划线例如1_000_000)时所做的所有情况,这将是一个很大的Regex,并且容易出错。 Integer()之所以是规范的,是因为Integer ()您可以确定Ruby认为整数文字的任何内容都会被接受,而其他所有内容都会被拒绝。与使用异常进行控制相比,复制语言已经给您的内容可能会带来更糟糕的代码味道。
09年

9
@Rado So正在重新发明轮子。
sepp2k 2014年

24

您可以做一个班轮:

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

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

甚至

int = Integer(str) rescue false

根据您要执行的操作,您还可以直接使用带有抢救子句的begin end块:

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

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

1
关于“作为控制流的异常”主题:由于我们不知道手头的方法将如何使用,因此我们无法真正判断异常是否适合。如果输入了字符串并且要求它是整数,那么提供非整数将保证有例外。尽管那时处理可能不是在同一方法中,我们可能只是执行Integer(str).times {| i | 放置i}或其他任何内容。
罗伯特·

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

4
它不会返回truefalse而是MatchData实例,以及nil
Stefan

它不是返回的内容,而是匹配的内容
Maciej Krasowski

5
用它包裹!!或使用present?,如果你需要一个布尔!!( "12".match /^(\d)+$/ )"12".match(/^(\d)+$/).present?(后者需要的Rails /的ActiveSupport)
mahemoff

1
此正则表达式不考虑符号:负数也是有效的整数。您现在正在测试有效的自然数或零。
Jochem Schulenklopper


8
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. 它没有前缀is_。我发现在问号方法上很愚蠢,我喜欢"04".integer?"foo".is_integer?
  2. 它使用sepp2k的明智解决方案,该解决方案 "01",例如。
  3. 面向对象,是的。

1
+1(将其命名为#integer?),-1(用于将字符串弄乱):-P
Avdi,2009年

1
它还会去哪里?integer?("a string")ftl。
8

2
String#integer?是每个Ruby编码器及其表弟都喜欢添加到该语言中的一种常见补丁,从而导致代码库具有三种不同的微妙不兼容的实现以及意外的损坏。我在大型Ruby项目上很难学到这一点。
09年

与上述相同的评论:异常不应用于控制流。
莎拉·梅

缺点:此解决方案浪费了一次转换。
罗伯特·克莱姆2014年

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也不错,但它会返回0Integer nil


我很高兴注意到您的回答。否则,当我需要“ Float”时,我会选择“ Integer”。
杰夫·齐夫科维奇

这很简单,但最好的答案!在大多数情况下,我们实际上并不需要对String类进行补丁。这最适合我!
Anh Nguyen

(浮点(值)抢救错误)?浮点数(值).to_s ==值?浮点数(值):整数(值):值
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

但是,正如其他人已经指出的那样,这不适用于八进制字符串。


2

Ruby 2.4具有Regexp#match?:(带有?

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

对于较早的Ruby版本,有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)+$/是用于在任何字符串中查找数字的正则表达式。您可以在http://rubular.com/上测试您的正则表达式和结果
  • 我们将其保存为常量 IntegerRegex以避免每次在该方法中使用它时不必要的内存分配。
  • integer?是一种应返回truefalse
  • match 是对字符串的方法,该方法根据参数中给定的regex表达式匹配出现的内容,并返回匹配的值或 nil
  • !!match方法的结果转换为等效的布尔值。
  • 并且在现有String类中声明该方法的是猴子补丁,它不会更改现有String功能的任何内容,而只是integer?在任何String对象上添加另一个命名的方法。

1
您能对此做些解释吗?
Stef

@stef-我做的也一样。如果您还有任何疑问,请告诉我。
萨钦(Sachin)

1

在上面的@rado答案上扩展也可以使用三元语句来强制返回真或假布尔值,而无需使用双刘海。诚然,双重逻辑否定版本更简洁,但对于新手(如我)来说可能更难阅读。

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

考虑到使用正则表达式会迫使Ruby做更多的工作,因此如果在循环中使用它会降低代码的速度。锚定表达式很有帮助,但是正则表达式仍然明显慢一些。
锡人

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

有关此处涉及的正则表达式的详细说明,请查看此博客文章:)


不需要调用,obj.is_a? String因为String#to_s会返回自身,我猜与.is_a?调用相比,不需要太多的处理。这样,您将仅在此行拨打一个电话,而不是一两个。同样,您可以直接将其包含!!number?方法内部,因为按照惯例,以结尾的方法名称?应该返回布尔值。问候!
乔瓦尼·贝努西

-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

-2

一根内胆 string.rb

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

-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 '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.