ChatGPT解决这个技术问题 Extra ChatGPT

Ruby 中的安全整数解析

我有一个字符串,比如 '123',我想将它转换为整数 123

我知道您可以简单地执行 some_string.to_i,但这会将 'lolipops' 转换为 0,这不是我想要的效果。当我尝试转换无效的东西时,我希望它在我的脸上炸开,并带有一个漂亮而痛苦的 Exception。否则,我无法区分有效的 0 和根本不是数字的东西。

编辑:我正在寻找没有正则表达式技巧的标准方法。


C
Community

Ruby 内置了这个功能:

Integer('1001')                                    # => 1001  
Integer('1001 nights')  
# ArgumentError: invalid value for Integer: "1001 nights"  

正如 Joseph Pecoraro 在回答中指出的那样,您可能需要注意有效的非十进制数字的字符串,例如以 0x 开头的十六进制数字和 0b 开头的二进制数字,以及以零开头的可能更棘手的数字将被解析为八进制。

Ruby 1.9.2 为 radix 添加了可选的第二个参数,因此可以避免上述问题:

Integer('23')                                     # => 23
Integer('0x23')                                   # => 35
Integer('023')                                    # => 19
Integer('0x23', 10)
# => #<ArgumentError: invalid value for Integer: "0x23">
Integer('023', 10)                                # => 23

P
Purfideas

这可能有效:

i.to_i if i.match(/^\d+$/)

PSA:在 Ruby 中,^$ have subtly different meanings 作为元字符,而不是大多数其他正则表达式风格。您可能打算改用 \A\Z
为了迂腐,根据@pje 提到不同的正则表达式锚点可能是不正确的,具体取决于所需的行为。而是考虑使用 \z 代替 \Z,因为大写 Z 锚的描述是:“匹配字符串结尾。如果字符串以换行符结尾,则它在换行符之前匹配” - ruby-doc.org/core-2.1.1/Regexp.html
J
Joseph Pecoraro

还要注意当前接受的解决方案可能对解析十六进制、八进制和二进制数产生的影响:

>> Integer('0x15')
# => 21  
>> Integer('0b10')
# => 2  
>> Integer('077')
# => 63

在 Ruby 中,以 0x0X 开头的数字是十六进制,0b0B 是二进制,只有 0 是八进制。如果这不是您想要的行为,您可能希望将其与其他一些检查字符串是否首先匹配模式的解决方案结合起来。像 /\d+/ 正则表达式等。


这就是我对转换的期望
在 Ruby 1.9 中,您可以将基数作为第二个参数传递。
i
ian

接受的解决方案的另一个意外行为(1.8、1.9 可以):

>> Integer(:foobar)
=> 26017
>> Integer(:yikes)
=> 26025

因此,如果您不确定传入的内容,请确保添加 .to_s


在 Ruby 1.9 中进行测试。 Integer(:foobar) => 无法将 Symbol 转换为 Integer (TypeError)
i
ian

我喜欢 Myron 的回答,但它患有“我不再使用 Java/C#,所以我再也不会使用继承”的 Ruby 疾病。打开任何类都可能充满危险,应谨慎使用,尤其是当它是 Ruby 核心库的一部分时。我并不是说永远不要使用它,但它通常很容易避免并且有更好的选择,例如

class IntegerInString < String

  def initialize( s )
    fail ArgumentError, "The string '#{s}' is not an integer in a string, it's just a string." unless s =~ /^\-?[0-9]+$/
    super
  end
end

然后,当您希望使用一个可能是数字的字符串时,很清楚您在做什么,并且您不会破坏任何核心类,例如

n = IntegerInString.new "2"
n.to_i
# => 2

IntegerInString.new "blob"
ArgumentError: The string 'blob' is not an integer in a string, it's just a string.

您可以在初始化中添加各种其他检查,例如检查二进制数等。但主要的是,Ruby 是为人服务的,而为人服务意味着清晰。通过变量名和类名命名一个对象会使事情变得更清晰。


佚名

我在上一个项目中不得不处理这个问题,我的实现是相似的,但有点不同:

class NotAnIntError < StandardError 
end

class String
  def is_int?    
    self =~ /^-?[0-9]+$/
  end

  def safe_to_i
    return self.to_i if is_int?
    raise NotAnIntError, "The string '#{self}' is not a valid integer.", caller
  end
end

class Integer
  def safe_to_i
    return self
  end            
end

class StringExtensions < Test::Unit::TestCase

  def test_is_int
    assert "98234".is_int?
    assert "-2342".is_int?
    assert "02342".is_int?
    assert !"+342".is_int?
    assert !"3-42".is_int?
    assert !"342.234".is_int?
    assert !"a342".is_int?
    assert !"342a".is_int?
  end

  def test_safe_to_i
    assert 234234 == 234234.safe_to_i
    assert 237 == "237".safe_to_i
    begin
      "a word".safe_to_i
      fail 'safe_to_i did not raise the expected error.'
    rescue NotAnIntError 
      # this is what we expect..
    end
  end

end

d
dusan
someString = "asdfasd123"
number = someString.to_i
if someString != number.to_s
  puts "oops, this isn't a number"
end

可能不是最干净的方法,但应该可以。


C
Community

回复:Chris's answer

您的实现让我们通过“1a”或“b2”之类的东西。这个怎么样:

def safeParse2(strToParse)
  if strToParse =~ /\A\d+\Z/
    strToParse.to_i
  else
    raise Exception
  end
end

["100", "1a", "b2", "t"].each do |number|
  begin
    puts safeParse2(number)
  rescue Exception
    puts "#{number} is invalid"
  end
end

这输出:

100
1a is invalid
b2 is invalid
t is invalid

为了迂腐,根据@pje 和使用的不同正则表达式锚的提及可能是不正确的,具体取决于所需的行为。而是考虑使用 \z 代替 \Z,因为大写 Z 锚的描述是:“匹配字符串结尾。如果字符串以换行符结尾,则它在换行符之前匹配” - ruby-doc.org/core-2.1.1/Regexp.html