严格将字符串转换为整数(或nil)

84

在Web编程中,数字通常出现为字符串形式。但是to_i会将"5abc"转换为5,而将"abc"转换为0,这两个结果都是错误的。为了避免这种情况,我写了以下代码:

def number_or_nil( s )
  number = s.to_i
  number = nil if (number.to_s != s)
  return number
end

有没有更简洁、更符合 Ruby 自然方式的方法来完成这种转换,并检测该字符串并非意图作为数字?


' 5 ' 应该是 5 还是 nil - Uri Agassi
问题不清楚。to_i有什么问题?什么是“这种转换”? - sawa
nil是我所期望的,尽管我可能可以接受5。 - Alistair Cockburn
那么 '5.0' 或者 '5.1' 呢?它们肯定是打算作为数字使用的,既然你这样提问,它们不也应该被转换为整数吗? - Daniël Knippers
3
整数是我唯一关心的事情,谢谢。个人认为“to_i”只会转换字符串的开头部分并将“abc”转换为零,这让我感到奇怪...但现在看起来这样做肯定有一个合理的原因。谢谢。 - Alistair Cockburn
这是它有用性的一个例子:[1,2,'cat'].reduce { |t,e| t + e.to_i } #=> 3。同样也是真实的,nil.to_i #=> 0,这可能很方便:a = [1,2,3].map { |e| e**2 if e < 3 } #=> [1, 4, nil]; a.reduce { |t,e| t + e.to_i } #=> 5 - Cary Swoveland
3个回答

126

使用 Integer(string)。

如果字符串无法转换为整数,它将引发 ArgumentError 错误。

Integer('5abc') #=> ArgumentError: invalid value for Integer(): "5abc"
Integer('5') #=> 5

如果您希望在无法将字符串转换时返回nil,则仍需要使用number_or_nil方法。

def number_or_nil(string)
  Integer(string || '')
rescue ArgumentError
  nil
end

你应该小心地从特定的异常中进行救援。裸的救援(例如 "rescue nil")会救援任何继承自StandardError的错误,并可能以你意想不到的方式干扰程序的执行。Integer()将引发ArgumentError,因此请指定它。

如果您不想处理异常,只想使用更短的number_or_nil版本,可以利用隐式返回值,并将其写为:

def number_or_nil(string)
  num = string.to_i
  num if num.to_s == string
end

number_or_nil '5' #=> 5
number_or_nil '5abc' #=> nil

这将按您的期望工作。


3
唯一需要注意的是,Integer(nil) 会引发 TypeError 错误,因此使用 Integer(string || '') 更加一致。 - RocketR
3
我在哪里可以找到Integer(string)的(官方)文档?据我所见,在官方的ruby Integer documentation中没有提到它。 - apepper
1
官方文档可在 http://ruby-doc.org/core/Kernel.html#method-i-Integer 找到。 - apepper
4
如果你输入Integer('5', 10),它会明确使用十进制。否则,如果你输入像 Integer('08') 这样的内容,它会因为假设错误的进制而导致错误。请注意保持原意,同时使语言通俗易懂。 - Levsero
7
现在到了“脑残时间”的环节:Integer('0010') => 8,所以不行——你不能只给出一个字符串,然后交叉你的手指,希望一切顺利,因为一个前导零这样简单的问题会让方法认为你正在处理二进制数字。 - bbozo
显示剩余3条评论

24
自Ruby 2.6起,核心函数Integer、Float等接受一个名为exception的关键字参数来完成此任务。
> Integer('42', exception: false)
=> 42
> Integer('x42', exception: false)
=> nil
> Integer('x42')
ArgumentError (invalid value for Integer(): "x42")
> Integer('', exception: false)
=> nil
> Integer('')
ArgumentError (invalid value for Integer(): "")
> Integer(nil, exception: false)
=> nil
> Integer(' 42 ', exception: false)
=> 42
> Integer(' 4 2 ', exception: false)
=> nil
请注意,Integer还让您控制进制,这是to_i不支持的功能:to_i
> '0xf'.to_i
=> 0
> Integer('0xf')
=> 15
> Integer('10', 8)
=> 8

当指定基数时,进制前缀(0x...等)必须保持一致(如果存在):

> Integer('0xf', 10)
=> ArgumentError(invalid value for Integer(): "0xf")
> Integer('0xf', 16)
=> 15
> Integer('f', 16)
=> 15

1
使用简单的正则表达式来检查 str 是否为整数。
def number_or_nil(str)
   str.to_i if str[/^-?\d+$/] and str.line.size == 1
end

1
由于输入来自网络,您应该更改答案以期望多行内容。 如果str[/\A\d+\z/],则str.to_i - Jim Gay
负整数,空格? - Cary Swoveland
1
修复了。我检查了整数的严格规则,不能有空格和换行符。 - hahcho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接