Programing

루비에서 안전한 정수 파싱

lottogame 2020. 6. 7. 00:43
반응형

루비에서 안전한 정수 파싱


라는 문자열이 있으며 '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.

You can add all sorts of other checks in the initialize, like checking for binary numbers etc. The main thing though, is that Ruby is for people and being for people means clarity. Naming an object via its variable name and its class name makes things much clearer.


I had to deal with this in my last project, and my implementation was similar, but a bit different:

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

Probably not the cleanest way to do it, but should work.


Re: Chris's answer

Your implementation let's things like "1a" or "b2" through. How about this instead:

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

This outputs:

100
1a is invalid
b2 is invalid
t is invalid

참고URL : https://stackoverflow.com/questions/49274/safe-integer-parsing-in-ruby

반응형