该程序的主要作用是对输入的一系列 IPv4 地址进行校验、去重、排序、聚合(地址压缩),并最终将处理后的结果以十进制 IP 地址格式输出。下面是程序各部分功能的详细分析:
1. 函数定义
1.1 D2B(IP)
十进制IP转为二进制IP
def D2B(IP):
tmp = IP.split('.')
Blist = []
Blist.append("{:08b}".format(int(tmp[0])))
Blist.append("{:08b}".format(int(tmp[1])))
Blist.append("{:08b}".format(int(tmp[2])))
Blist.append("{:08b}".format(int(tmp[3].split('/')[0])))
Blist.append(int(tmp[3].split('/')[1]))
BIPtmp = ''
for i in Blist:
BIPtmp += str(i)
BIP = BIPtmp[:32:] + '/' + BIPtmp[32::]
return BIP
- 功能:将十进制的 IPv4 地址(带子网掩码,如
192.168.1.0/24
)转换为二进制格式。 - 步骤:
- 将输入的 IP 地址按
.
分割成四部分。 - 把每部分转换为 8 位二进制字符串。
- 提取子网掩码部分。
- 将二进制字符串和子网掩码拼接成完整的二进制 IP 地址。
- 将输入的 IP 地址按
1.2 B2D(IP)
二进制IP转为十进制IP
def B2D(IP):
tmp = IP.split('/')
Dlist = []
Dlist.append(int(tmp[0][:8:], 2))
Dlist.append(int(tmp[0][8:16:], 2))
Dlist.append(int(tmp[0][16:24:], 2))
Dlist.append(int(tmp[0][24:32:], 2))
DIPtmp = ''
for i in Dlist:
DIPtmp += str(i)
DIPtmp += '.'
DIP = DIPtmp[:-1:] + '/' + str(tmp[1])
return DIP
- 功能:将二进制的 IPv4 地址(带子网掩码)转换为十进制格式。
- 步骤:
- 将输入的二进制 IP 地址按
/
分割成 IP 部分和子网掩码部分。 - 将 IP 部分每 8 位转换为十进制整数。
- 将十进制整数用
.
连接起来,并拼接子网掩码,形成完整的十进制 IP 地址。
- 将输入的二进制 IP 地址按
1.3 check(IN)
校验
def check(IN):
IN1 = IN.split('/')
IN2 = IN1[0].split('.')
IN2.append(IN1[1])
IN2 = list(map(int, IN2))
if len(IN2) != 5:
return False
for i in IN2[0:4:]:
if i not in range(0, 256):
return False
if IN2[4] not in range(0, 33):
return False
return True
- 功能:检查输入的 IPv4 地址(带子网掩码)格式是否正确。
- 步骤:
- 检查输入的 IP 地址是否能正确分割成 5 个部分(4 个 IP 段和 1 个子网掩码)。
- 检查每个 IP 段是否在 0 到 255 之间。
- 检查子网掩码是否在 0 到 32 之间。
1.4 打印函数
def print1(element):
a, b0 = element.split('/')
b = int(b0)
print(a[:b], a[b:] + '/' + b0)
def print2(list):
for i in list:
print1(i)
def print3(list):
for i in list:
print(i)
print1(element)
:将 IP 地址按子网掩码分割成网络前缀和剩余部分并打印。print2(list)
:对列表中的每个 IP 地址调用print1
函数进行打印。print3(list)
:打印列表中的每个元素。
1.5 地址压缩函数
功能概述
aggregation
函数的主要功能是对输入的二进制 IP 地址前缀列表进行聚合(地址压缩)操作。在网络地址规划中,地址聚合可以将多个相邻的 IP 地址块合并成一个更大的地址块,从而减少路由表的条目数量,提高路由效率。该函数通过递归的方式,不断检查相邻的 IP 地址前缀是否可以合并,直到无法再进行合并为止。
代码注解
def aggregation(INlist=[], flag=1):
# 如果 flag 为 0,表示上一轮没有发生合并操作,直接返回输入列表
if flag == 0:
return INlist
# 获取列表的长度
i = len(INlist) - 1
# 初始化 flag 为 0,表示当前轮次还没有发生合并操作
flag = 0
# 从列表末尾开始向前遍历
while i > 0:
# 获取当前元素和前一个元素
IP = INlist[i - 1]
IP2 = INlist[i]
# 提取当前元素和前一个元素的前缀(去掉最后一位)
prefix = IP[:-1]
prefix2 = IP2[:-1]
# 如果两个前缀相同,说明可以合并
if prefix == prefix2:
# 删除当前元素
del INlist[i]
# 将前一个元素更新为合并后的前缀
INlist[i - 1] = prefix
# 跳过下一个元素,因为已经合并了
i -= 2
# 设置 flag 为 1,表示发生了合并操作
flag = 1
else:
# 如果不能合并,继续向前遍历
i -= 1
# 递归调用 aggregation 函数,继续进行合并操作
return aggregation(INlist, flag)
测试用例及结果
测试用例 1:简单合并
# 输入的二进制 IP 地址前缀列表
input_list = [
'10000000000000000000000000000000',
'10000000000000000000000000000001']
# 调用 aggregation 函数进行合并
result = aggregation(input_list)
# 输出结果
print("合并后的结果:", result)
预期结果:
合并后的结果: ['1000000000000000000000000000000']
解释:输入的两个前缀只有最后一位不同,因此可以合并成一个更短的前缀。
测试用例 2:无法合并
input_list = [
'10000000000000000000000000000000',
'11000000000000000000000000000000']
result = aggregation(input_list)
print("合并后的结果:", result)
预期结果:
合并后的结果: [
'10000000000000000000000000000000',
'11000000000000000000000000000000'
]
解释:输入的两个前缀差异较大,无法进行合并,因此结果与输入相同。
测试用例 3:多次合并
input_list = [
'10000000000000000000000000000000',
'10000000000000000000000000000001',
'10000000000000000000000000000010',
'10000000000000000000000000000011'
]
result = aggregation(input_list)
print("合并后的结果:", result)
预期结果:
合并后的结果: ['100000000000000000000000000000']
解释:输入的四个前缀可以逐步合并,最终得到一个更短的前缀。
2. 主函数 main()
def main():
while True:
INPUT = input() #输入结束
if INPUT == '':
break
if check(INPUT): #校验&去重
tail = int(INPUT.split('/')[1])
pBIP = D2B(INPUT)[0:tail]
pINjihe.add(pBIP)
else: #校验未通过
continue
pINlist = list(pINjihe)
pINlist.sort() #排序,方便后续进行地址聚合
pOUTlist = aggregation(pINlist) #地址聚合
for i in pOUTlist:
tail = len(i)
OUTlist.append(i + (32 - tail) * '0' + '/' + str(tail)) #更改为标准二进制格式
print3(list(map(B2D, OUTlist))) #更改为十进制格式并输出
- 主程序逻辑:负责读取输入、校验、去重、排序、聚合和输出结果。
- 步骤:
- 循环读取用户输入,直到输入为空。
- 对每个输入进行格式校验,如果校验通过,将其转换为二进制并提取网络前缀,添加到集合
pINjihe
中进行去重。 - 将集合转换为列表并排序。
- 对排序后的列表进行地址聚合。
- 将聚合后的结果补充为标准的 32 位二进制 IP 地址,并添加到
OUTlist
中。 - 将
OUTlist
中的二进制 IP 地址转换为十进制格式并输出。
该程序可以帮助用户对输入的 IPv4 地址进行处理,去除重复地址,合并相邻的网络前缀,最终输出经过优化的 IPv4 地址列表。
3. 测试用例
输入:
192.168.238.0/24
192.168.239.0/24
192.168.240.0/24
192.168.241.0/24
192.168.242.0/24
192.168.243.0/24
192.168.244.0/24
192.168.245.0/24
192.168.246.0/24
192.168.247.0/24
192.168.248.0/24
192.168.249.0/24
192.168.250.0/24
192.168.251.0/24
192.168.252.0/24
192.168.253.0/24
192.168.254.0/24
192.168.255.0/24
输出:
192.168.238.0/23
192.168.238.0/23
192.168.240.0/20
4. 程序源码
pINjihe=set()
OUTlist=[]
#将十进制IP转化为二进制
def D2B(IP):
tmp=IP.split('.')
Blist=[]
Blist.append("{:08b}".format(int(tmp[0])))
Blist.append("{:08b}".format(int(tmp[1])))
Blist.append("{:08b}".format(int(tmp[2])))
Blist.append("{:08b}".format(int(tmp[3].split('/')[0])))
Blist.append(int(tmp[3].split('/')[1]))
BIPtmp=''
for i in Blist:
BIPtmp+=str(i)
BIP=BIPtmp[:32:]+'/'+BIPtmp[32::]
return BIP
#将二进制IP转化为十进制
def B2D(IP):
tmp=IP.split('/')
Dlist=[]
Dlist.append(int(tmp[0][:8:],2))
Dlist.append(int(tmp[0][8:16:],2))
Dlist.append(int(tmp[0][16:24:],2))
Dlist.append(int(tmp[0][24:32:],2))
DIPtmp=''
for i in Dlist:
DIPtmp+=str(i)
DIPtmp+='.'
DIP=DIPtmp[:-1:]+'/'+str(tmp[1])
return DIP
#IP地址格式检测
def check(IN):
IN1=IN.split('/')
IN2=IN1[0].split('.')
IN2.append(IN1[1])
IN2=list(map(int,IN2))
if len(IN2) != 5: #检测格式
return False
for i in IN2[0:4:]: #检测IP地址范围
if i not in range(0,256):
return False
if IN2[4] not in range(0,33): #检测网络位范围
return False
return True
#各种打印方法
def print1(element):
a,b0=element.split('/')
b=int(b0)
print(a[:b],a[b:]+'/'+b0)
def print2(list):
for i in list:
print1(i)
def print3(list):
for i in list:
print(i)
#地址压缩函数
def aggregation(INlist=[],flag=1):
if flag==0:
return INlist
i=len(INlist)-1
flag=0
while i>0:
IP=INlist[i-1]
IP2=INlist[i]
prefix=IP[:-1]
prefix2=IP2[:-1]
if prefix==prefix2:
del INlist[i]
INlist[i-1]=prefix
i-=2
flag=1
else:
i-=1
return aggregation(INlist,flag)
def main():
while True:
INPUT=input()
if INPUT == '': #输入结束
break
if check(INPUT): #校验&去重
tail=int(INPUT.split('/')[1])
pBIP=D2B(INPUT)[0:tail]
pINjihe.add(pBIP)
else: #校验未通过
continue
pINlist=list(pINjihe)
pINlist.sort() #排序
pOUTlist=aggregation(pINlist) #压缩
for i in pOUTlist:
tail=len(i)
OUTlist.append( i+ (32-tail)*'0' +'/'+ str(tail) ) #更改为标准二进制格式
print3(list(map(B2D,OUTlist))) #更改为十进制格式并输出
main()