루비에서 안전한 정수 파싱


160

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

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

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

답변:


234

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

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

27

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

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

8
PSA : Ruby에서, 대부분의 다른 정규 표현식 ^$ 는 다르게 메타 문자 와 미묘한 의미 가 있습니다. 당신은 아마 사용에 의미 \A\Z대신.
pje

1
@pje에 따라 다른 정규 표현식 앵커에 대한 언급은 원하는 행동에 따라 정확하지 않을 수 있습니다. 대신 대문자 Z 앵커에 대한 설명으로 대신 사용 \z을 고려하십시오 \Z. "문자열 끝과 일치합니다. 문자열이 줄 바꿈으로 끝나는 경우 줄 바꿈 직전에 일치합니다" -ruby-doc.org/core-2.1.1/Regexp .html
Del

24

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

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

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


1
그래도 변환에서 기대하는 것입니다
wvdschel

5
Ruby 1.9에서는 기본을 두 번째 인수로 전달할 수 있습니다.
앤드류 그림

17

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

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

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


7
Ruby 1.9에서 테스트하십시오. Integer (: foobar) => Symbol을 Integer로 변환 할 수 없음 (TypeError)
GutenYe

9

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는 사람들을위한 것이며 사람들을위한 것은 명확성을 의미한다는 것 입니다. 변수 이름 클래스 이름을 통해 객체의 이름을 지정하면 훨씬 명확 해집니다.


6

마지막 프로젝트 에서이 문제를 해결해야했으며 구현은 비슷하지만 약간 다릅니다.

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

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

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


1

재 : 크리스의 대답

당신의 구현은 "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
Del
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.