Rubyでの安全な整数解析


160

たとえば'123'、文字列があり、それを整数に変換したい123

私はあなたが簡単にできることを知っていますが、それはにsome_string.to_i変換さ'lolipops'れますが0、これは私が考えている効果ではありません。無効で何かを変換しようとしたときに、それが私の顔で爆破されて、素敵で苦痛なものになって欲しいException。そうでなければ、私は有効な0ものとまったく数字ではないものを区別できません。

編集:私は正規表現を使わずに、標準的な方法を探していました。

回答:


234

Rubyにはこの機能が組み込まれています。

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

Joseph Pecoraroの回答で述べたように0x、16進数と0b2 進数で始まるものなど、有効な非10進数の文字列や、0で始まる8進数として解析されるよりトリッキーな数値を監視する必要がある場合があります。

Ruby 1.9.2では、基数にオプションの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代わりに。
2013年

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

始まるRubyの番号で0xまたは0X進をしている、0bまたは0B単にバイナリであり、0オクタルです。これが望ましい動作ではない場合は、最初に文字列がパターンに一致するかどうかをチェックする他のいくつかのソリューションと組み合わせることができます。同様に/\d+/、正規表現など


1
それは私が変換から期待することです
wvdschel 08

5
Ruby 1.9では、ベースを2番目の引数として渡すことができます。
Andrew Grimm

17

受け入れられたソリューションでの別の予期しない動作(1.8、1.9では問題ありません):

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

そのため、何が渡されるのかわからない場合は、必ずを追加してください.to_s


7
Ruby 1.9でテストします。Integer(:foobar)=>シンボルを整数に変換できません(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.

2進数のチェックなど、初期化で他のあらゆる種類のチェックを追加できます。ただし、主なことは、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

Re:クリスの答え

あなたの実装は、「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.