题目
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
- 需要把”128.32.10.1”这个字符串里的每一个ip数字转换为二进制。
- 把4段二进制拼接在一起形成一个32位二进制串。
- 把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
- gsub方法有一个用法就是可以传递块,这样就避免了我另外去迭代处理。/(\d)+/,这个正则不太严谨,只是取数字,但没有判断不合法的IP地址,这里就不考虑那么多了。
- “0”*(8-i.size)« i 这段代码是个笨方法,是为了给转换为二进制的IP数字补足8位。
- 128.to_s(2)是十进制转二进制的一个方法, “1010”.to_i(2)是二进制转十进制的方法。
方案2
使用字符串格式化
def ip_to_int32(ip)
("%02x%02x%02x%02x" % ip.split('.')).to_i(16)
end
- “%02x”是把一个字符串格式化为16进制,2为指定的输出字段的宽度.如果位数小于2,则左端补0
- % 操作符, 用于字符串格式化。
- to_i(16), 把16进制转换为10进制。
方案3
def ip_to_int32(ip)
("%08b"*4 % ip.split('.')).to_i(2)
end
- 使用2进制、8进制、16进制的指示符分别是(
b', o', x', X'
)
- 所以,”%08b”是把一个十进制数格式化为一个二进制数,8位宽度,如果位数小于8,则左端补0.
方案4
使用位操作
def ip_to_int32(ip)
ip.split('.').inject(0) { |total, val| (total << 8) + val.to_i}
end
- IP地址都是基于256来计算的
- 128 * (256)3 + 32 * (256)2 + 10 * (256)1 + 1 * (256)0 = ?
- 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
这是我自己的答案
- 先把所有的数字转换成2进制不够的位数用0补全(字符串),得到 10000000,00100000,00001010,00000001
- 组成 32 位的 2进制数(字符串) 10000000001000000000101000000001
- 把上一步的数字转换成 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
。
相关链接