IP 转换器

题目

Take the following IPv4 address: 128.32.10.1 This address has 4 octets where each octet is a single byte (or 8 bits).

1st octet 128 has the binary representation: 10000000 2nd octet 32 has the binary representation: 00100000 3rd octet 10 has the binary representation: 00001010 4th octet 1 has the binary representation: 00000001 So 128.32.10.1 == 10000000.00100000.00001010.00000001

Because the above IP address has 32 bits, we can represent it as the 32 bit number: 2149583361.

Write a function ip_to_int32(ip) that takes an IPv4 address and returns a 32 bit number.

ip_to_int32(“128.32.10.1”) => 2149583361

有一个IPv4地址: 128.32.10.1。 这个地址实际由4个8位的字节组成:

第一个128,它的二进制是: 10000000 第二个32, 它的二进制是: 00100000 第三个10, 它的二进制是: 00001010 第四个1, 它的二进制是: 00000001

所以, 128.32.10.1 相当于 10000000.00100000.00001010.00000001

这样的话,IP地址就是由32位的了,于是我们就根据这32位字节得到一个数字:2149583361。

要求:

写一个函数:ip_to_int32(ip) 可以让这个IPv4的地址返回那个数字: iptoint32(“128.32.10.1”) = 2149583361

解题思路

这里隐含的知识是: 为什么要把IP转换为一个数字呢?

TCP/IP协议规定,IP地址是由32位二进制数组成,简单来说,是为了便于查询或做一些根据IP设置的登陆限制,比如一些IP数据库,里面存储的就是根据32位二进制转化成的数。

在知道这个基础知识的前提下,这个题目才变得有意义。

方案1

  1. 需要把”128.32.10.1”这个字符串里的每一个ip数字转换为二进制。
  2. 把4段二进制拼接在一起形成一个32位二进制串。
  3. 把32位二进制串变成十进制。
def ip_to_int32(ip)
  ip.gsub(/(\d+)/){|i|i = i.to_i.to_s(2); "0"*(8-i.size)<< i}.gsub(/\./, "").to_i(2)
end
  1. gsub方法有一个用法就是可以传递块,这样就避免了我另外去迭代处理。/(\d)+/,这个正则不太严谨,只是取数字,但没有判断不合法的IP地址,这里就不考虑那么多了。
  2. “0”*(8-i.size)« i 这段代码是个笨方法,是为了给转换为二进制的IP数字补足8位。
  3. 128.to_s(2)是十进制转二进制的一个方法, “1010”.to_i(2)是二进制转十进制的方法。

方案2

使用字符串格式化

def ip_to_int32(ip)
  ("%02x%02x%02x%02x" % ip.split('.')).to_i(16)
end
  1. “%02x”是把一个字符串格式化为16进制,2为指定的输出字段的宽度.如果位数小于2,则左端补0
  2. % 操作符, 用于字符串格式化。
  3. to_i(16), 把16进制转换为10进制。

方案3

def ip_to_int32(ip)
  ("%08b"*4 % ip.split('.')).to_i(2)
end
  1. 使用2进制、8进制、16进制的指示符分别是(b', o', x', X')
  2. 所以,”%08b”是把一个十进制数格式化为一个二进制数,8位宽度,如果位数小于8,则左端补0.

方案4

使用位操作

def ip_to_int32(ip)
  ip.split('.').inject(0) { |total, val| (total << 8) + val.to_i}
end
  1. IP地址都是基于256来计算的
  2. 128 * (256)3 + 32 * (256)2 + 10 * (256)1 + 1 * (256)0 = ?
  3. 2147483648 + 2097152 + 2560 + 1 = 2149583361

方案5

使用 ruby 内建的方法是产品环境最该采用的方案

require 'ipaddr'
def ip_to_int32(ip)
  IPAddr.new(ip).to_i
end

方案6

def ip_to_int32(ip_address)
  ip_address.split(".").inject('') { |ip, str| ip + str.to_i.to_s(2).rjust(8, "0") }.to_i(2)
end

这是我自己的答案

  1. 先把所有的数字转换成2进制不够的位数用0补全(字符串),得到 10000000,00100000,00001010,00000001
  2. 组成 32 位的 2进制数(字符串) 10000000001000000000101000000001
  3. 把上一步的数字转换成 10 进制的数字

相关知识点

1个英文字母等于多少字节

字符就是几个字母,比如 a 和 A 都是一个字符。aa 就是两个字符。 每个字节是由8位组成的。位是最小的单位了,叫做 bit。而字节呢,叫做 byte。 所以,一个英文字母,无论大写和小写都是一个字符,占一个字节8位。 一个汉字是一个字符、两个字节,16位。

Fixnum 的位移操作

根据上面的问题,我们试试

# 1.class is Fixnum
puts 1.size
#=> 8

Fixnum 的 size 方法 Returns the number of bytes in the machine representation of fix.

1 的内存地址中表示方法是 00000001,我们可以位移 1 位,1 << 1,得到的结果在内存中是 00000010。转换成10进制的数字是 1 * 2 = 2。如果位移 8 位,1 << 8,得到的结果在内存中是 10000000。转换成10进制的数字是 1 * 256(2的8次方) = 256

相关链接

如果觉得我的文章对您有用,请在支付宝公益平台找个项目捐点钱。 @Victor Sep 30, 2014

奉献爱心