[ruby] 루비에서 안전한 정수 파싱

라는 문자열 '123'이 있으며 정수로 변환하고 싶습니다 123.

나는 당신이 단순히 할 수 있다는 것을 알고 some_string.to_i있지만 그것은로 변환 'lolipops'합니다 0. 이것은 내가 생각한 효과가 아닙니다. 멋지고 고통스러운 잘못된 것을 변환하려고 할 때 얼굴이 터지기를 원합니다 Exception. 그렇지 않으면, 유효한 0숫자와 전혀 숫자가 아닌 것을 구별 할 수 없습니다 .

편집 : 정규식 속임수없이 표준 방식을 찾고있었습니다.



답변

루비에는이 기능이 내장되어 있습니다 :

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

Joseph Pecoraro의 답변에서 언급했듯이 0x16 진수 및 0b2 진수로 시작하는 것과 같이 10 진수가 아닌 유효한 문자열 과 0으로 시작하는 더 까다로운 숫자를 8 진수로 구문 분석 할 수 있습니다.

Ruby 1.9.2는 기수에 대한 두 번째 인수 옵션을 추가하여 위의 문제를 피할 수 있습니다.

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


답변

이것은 작동 할 수 있습니다 :

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


답변

또한 현재 승인 된 솔루션이 16 진, 8 진 및 2 진 숫자 구문 분석에 미칠 수있는 영향에 유의하십시오.

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

루비에서 시작 0x하거나 0X16 진수 0b이거나 0B2 0진수이며 8 진수입니다. 이것이 원하는 동작이 아닌 경우 문자열을 패턴과 먼저 일치하는지 확인하는 다른 솔루션과 결합 할 수 있습니다. 등 /\d+/등 정규 표현식,


답변

허용되는 솔루션에 대한 또 다른 예기치 않은 동작 (1.8, 1.9 사용) :

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

전달되는 내용이 확실하지 않은 경우을 추가해야합니다 .to_s.


답변

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


답변

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

아마도 가장 깨끗한 방법은 아니지만 작동해야합니다.