如何从可能带有前导零的字符串中解析出数字?

9
在Ruby中,我正在解析以下格式的日期:24092008。我想将每个部分(年份、月份、日期)转换为数字。
我使用正则表达式将它们拆分成三个字符串,并将它们传递给整数构造函数。
  date =~ /^([\d]{2})([\d]{2})([\d]{4})/
  year = Integer($3)
  month = Integer($2)
  day = Integer($1)

当它到达月份线时,会崩溃并显示以下内容:
`Integer': invalid value for Integer: "09" (ArgumentError)

我花了一些时间才意识到它将前导零解释为八进制,而09不是有效的八进制数字(使用“07”可以正常工作)。

这个问题是否有优雅的解决方案,或者我应该只测试小于10的数字并先删除零呢?

谢谢。

4个回答

15

我对正则表达式不熟悉,如果我的回答有误请见谅。我一直认为$3、$2和$1是字符串。以下是我在IRB中复制问题的方法:

irb(main):003:0> Integer("04")
=> 4
irb(main):004:0> Integer("09")
ArgumentError: invalid value for Integer: "09"
    from (irb):4:in `Integer'
    from (irb):4
    from :0

但看起来.to_i没有同样的问题:

irb(main):005:0> "04".to_i
=> 4
irb(main):006:0> "09".to_i
=> 9

1
@Atiaxi:抛出“无效值”是因为“09”不是有效的八进制数。前导零通常意味着它是八进制的,而0-7是八进制中有效的数字。:obj.to_i转换时假定为十进制。 - user7116
1
"String#to_i" 会忽略末尾的非数字字符。"123abc".to_i # => 123。这可能不是期望的结果。 - Gareth
1
在 Ruby 1.9 中,您可以明确指定十进制:Integer("09", 10) #=> 9 - Stefan

9

指定十进制

明确告诉Ruby,将该字符串解释为十进制数字。

Integer("09", 10) # => 9

这比.to_i更严格,如果你想要严谨的话。
"123abc".to_i # => 123
Integer("123abc", 10) # => ArgumentError

我是如何找出这个问题的

irb中,method(:Integer)返回#<Method: Object(Kernel)#Integer>。这告诉我Kernel拥有这个方法,于是我查阅了关于Kernel的文档。方法签名显示它接受第二个参数作为基数。


1
也许可以使用 (0([\d])|([1-9][\d])) 替换 ([\d]{2})。 您可能需要在 $1、$2 和 $3 的位置使用 $2、$4 和 $5。
或者,如果您的正则表达式支持 (?:...),则可以使用 (?:0([\d])|([1-9][\d]))
由于 Ruby 的正则表达式源自 Perl,因此后一种版本应该也可以使用。

问题在于现在有太多匹配项了。我认为在这种情况下,to_i方法是最好的选择,因为它符合KISS原则。 - Vinko Vrsalovic

0

不要直接检查任何带有前导零的整数。例如:

Integer("08016") #=> ArgumentError: invalid value for Integer(): "08016"

创建一个方法来检查和解决前导零问题:

def is_numeric(data)
  _is_numeric = true if Integer(data) rescue false

  # To deal with Integers with leading 0
  if !_is_numeric
    _is_numeric = data.split("").all?{|q| Integer(q.to_i).to_s == q }
  end

  _is_numeric
end

is_numeric("08016") #=> true is_numeric("A8016") #=> false

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