サブネット計算のお供に


計算問題が嫌いなので作ってみました。ただこれを使うとあまりサブネット計算の学習にはならないという問題を抱えています。
Ping-tのサブネットの計算問題で試してみて合っていたので、多分問題ないと思います。
　　
32bit値のところで参考にさせていただきました。
[@harasou@github シェルスクリプトで IPアドレス計算] (http://qiita.com/harasou@github/items/5c14c335388f70e178f5 "シェルスクリプトで IPアドレス計算")
　　
基本的に10進数のIPアドレスを入れて使う用途になっていますが、一応2進数表記から10進数表記に変換することもできます。
　　
###**色々と問題がありましたので下の方に修正版を追記してあります。**

```ruby
str = ARGV[0]

if str.match(/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/) then

  num = str.split(".")

  #入力したIPアドレスの32Bit値を保管
  @hom = (num[0].to_i << 24) + (num[1].to_i << 16) + (num[2].to_i << 8) + (num[3].to_i)

  #２進数表記に変換
  num.each_with_index do |n, count|
    if count == 3
      printf("%08d\n",  "#{n.to_i.to_s(2)}")
      break
    end
    @z = printf("%08d.","#{n.to_i.to_s(2)}")
  end

  puts "ネットワークの計算をしますか？"
  puts "計算する場合はCIDR値を入力してください。"
  print "==> "
  cidr = STDIN.gets.chomp

  #保管している32Bit値から論理和を計算
  exit unless cidr.match(/[1-31]/)
  cidr32 = (( 0xFFFFFFFF ^ ((2 ** (32-cidr.to_i)-1) )))
  nwaddr = cidr32 & @hom
  puts "ネットワークアドレス"
  printf "%d.%d.%d.%d\n", (nwaddr >> 24), ((nwaddr >> 16) & 0xFF), ((nwaddr >> 8) & 0xFF), (nwaddr & 0xFF)
  #ネットワークアドレスの排他的論理和と論理和
  puts "ブロードキャストアドレス"
  broad = ((cidr32 ^ 0xFFFFFFFF) | @hom)
  printf "%d.%d.%d.%d\n", (broad >> 24), ((broad >> 16) & 0xFF), ((broad >> 8) & 0xFF), (broad & 0xFF)

elsif str.match(/([01]{1,8})\.([01]{1,8})\.([01]{1,8})\.([01]{1,8}$)/) then

  num = str.split(".")

  num.each_with_index do |n, count|
    m = n.to_i(2)
    if count == 3
      puts "#{m}"
      break
    end
    print "#{m}."
  end
end
```

#使い方  
引数に10進のIPアドレス。CIDRの確認で止まるので自分で考えてみたりするもよし・・。

```
$ ruby subnet2.rb 210.150.30.10
11010010.10010110.00011110.00001010
ネットワークの計算をしますか？
計算する場合はCIDR値を入力してください。
==> 28
ネットワークアドレス
210.150.30.0
ブロードキャストアドレス
210.150.30.15
```

ネットワーク計算が不要なら1〜31以外の数字でも空エンターでも叩くとEXITします。

```
$ ruby subnet2.rb 192.168.24.133
11000000.10101000.00011000.10000101
ネットワークの計算をしますか？
計算する場合はCIDR値を入力してください。
==>
```

上の2個を例にして2進数表記からの変換

```
$ ruby subnet2.rb 11010010.10010110.00011110.00001010
210.150.30.10

$ ruby subnet2.rb 11000000.10101000.00011000.10000101
192.168.24.133
```

こんな感じです。
　　
　　
はっ！スクリプトを書きだした前半と後半でprintfの記述の仕方が変わっている！
Σ(￣ロ￣lll)がびーん

##2015/03/17　追記

@scivola さん、@riocampos さん
いつもありがとうございます。

指摘いただいた間違い部分と組み込み系のメソッドの利用部分を使って修正してみました。
みましたというかモノマネですが^_^;
長くなりますが比較用のコメントアウトVerと、スッキリVerの2個載せます。

###修正Ver（比較用コメントアウトのみ）

```ruby
str = ARGV[0]

case str
when /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/

  #nums = str.split(".")
  nums = [$1, $2, $3, $4].map(&:to_i)

  #IPアドレスの最終確認
  #nums.each do |x|
  #  if x.to_i > 255
  #    puts "不正な入力です。"
  #    exit
  #  end
  #end
  if nums.any? { |x| x.to_i > 255 }
      puts "不正な入力です。"
        exit
  end

  #入力したIPアドレスの32Bit値を保管
  #p @hom = (nums[0].to_i << 24) + (nums[1].to_i << 16) + (nums[2].to_i << 8) + (nums[3].to_i)
  @hom = nums.inject{|r, x| r * 256 + x}

  #２進数表記に変換
  puts nums.map{|x| format('%08b', x)}.join('.')

  #２進数表記に変換
  #nums.each_with_index do |n, count|
  #  if count == 3
  #    printf("%08d\n",  "#{n.to_i.to_s(2)}")
  #    break
  #  end
  #  printf("%08d.","#{n.to_i.to_s(2)}")
  #end

  puts "ネットワークの計算をしますか？"
  puts "計算する場合はCIDR値を入力してください。"
  print "==> "
  cidr = STDIN.gets.chomp

  #保管している32Bit値から論理和を計算
  exit unless cidr.match(/\A\d+\z/) && (1..31).include?(cidr.to_i)
  cidr32 = (( 0xFFFFFFFF ^ ((2 ** (32-cidr.to_i)-1) )))
  nwaddr = cidr32 & @hom
  puts "ネットワークアドレス"
  #printf "%d.%d.%d.%d\n", (nwaddr >> 24), ((nwaddr >> 16) & 0xFF), ((nwaddr >> 8) & 0xFF), (nwaddr & 0xFF)
  puts [24, 16, 8, 0].map{|n| (nwaddr >> n) & 0xFF}.join('.')
  #ネットワークアドレスの排他的論理和と論理和
  puts "ブロードキャストアドレス"
  broad = ((cidr32 ^ 0xFFFFFFFF) | @hom)
  #printf "%d.%d.%d.%d\n", (broad >> 24), ((broad >> 16) & 0xFF), ((broad >> 8) & 0xFF), (broad & 0xFF)
  puts [24, 16, 8, 0].map{|n| (broad >> n) &0xFF}.join('.')

when /([01]{1,8})\.([01]{1,8})\.([01]{1,8})\.([01]{1,8}$)/

  #2進数表記から10進数表記に変換
  puts nums = [$1, $2, $3, $4].map{|s| s.to_i(2)}.join('.')

  #2進数表記から10進数表記に変換
  #nums = str.split(".")
  #nums.each_with_index do |n, count|
  #  m = n.to_i(2)
  #  if count == 3
  #    puts "#{m}"
  #    break
  #  end
  #  print "#{m}."
  #end
end

```

###スッキリVer

```ruby
str = ARGV[0]

case str
when /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/

  nums = [$1, $2, $3, $4].map(&:to_i)

  #IPアドレスの最終確認
  if nums.any? { |x| x.to_i > 255 }
      puts "不正な入力です。"
        exit
  end

  #入力したIPアドレスの32Bit値を保管
  @hom = nums.inject{|r, x| r * 256 + x}

  #２進数表記に変換
  puts nums.map{|x| format('%08b', x)}.join('.')
  puts "ネットワークの計算をしますか？"
  puts "計算する場合はCIDR値を入力してください。"
  print "==> "
  cidr = STDIN.gets.chomp

  #保管している32Bit値から論理和を計算
  exit unless cidr.match(/\A\d+\z/) && (1..31).include?(cidr.to_i)
  cidr32 = (( 0xFFFFFFFF ^ ((2 ** (32-cidr.to_i)-1) )))
  nwaddr = cidr32 & @hom
  puts "ネットワークアドレス"
  puts [24, 16, 8, 0].map{|n| (nwaddr >> n) & 0xFF}.join('.')

  #ネットワークアドレスの排他的論理和と論理和
  puts "ブロードキャストアドレス"
  broad = ((cidr32 ^ 0xFFFFFFFF) | @hom)
  puts [24, 16, 8, 0].map{|n| (broad >> n) &0xFF}.join('.')

when /([01]{1,8})\.([01]{1,8})\.([01]{1,8})\.([01]{1,8}$)/

  puts nums = [$1, $2, $3, $4].map{|s| s.to_i(2)}.join('.')

end

```

###不具合の例
正規表現が間違っており、おかしなものが入った場合はEXITと思って書いていましたがこんなのが通ってしまっていました。

```
計算する場合はCIDR値を入力してください。
==> 2222
subnet2.rb:29:in `^': Rational can't be coerced into Rational (TypeError)
	from subnet2.rb:29:in `<main>'

```

もういっちょは何も入れずでエラーが。

```
$ ruby subnet2.rb
subnet2.rb:6:in `<main>': undefined method `match' for nil:NilClass (NoMethodError)

```

などなど。