当前位置: 首页 > 工具软件 > SPIP > 使用案例 >

IP地址IP网段合并

高恺
2023-12-01
# -*- coding: utf8 -*-
# 有中文变量名,请使用Python3运行
# 可显示/关闭运行过程

## 终端显示颜色
import os
if os.name == 'nt':       # Windows
    import ctypes,sys
    STD_OUTPUT_HANDLE = -11
    
    # Windows CMD命令行 字体颜色定义 text colors
    黑字 = 0x00 # black.
    FOREGROUND_DARKBLUE = 0x01 # dark blue.
    FOREGROUND_DARKGREEN = 0x02 # dark green.
    FOREGROUND_DARKSKYBLUE = 0x03 # dark skyblue.
    FOREGROUND_DARKRED = 0x04 # dark red.
    FOREGROUND_DARKPINK = 0x05 # dark pink.
    FOREGROUND_DARKYELLOW = 0x06 # dark yellow.
    FOREGROUND_DARKWHITE = 0x07 # dark white.
    FOREGROUND_DARKGRAY = 0x08 # dark gray.
    FOREGROUND_BLUE = 0x09 # blue.
    FOREGROUND_GREEN = 0x0a # green.
    FOREGROUND_SKYBLUE = 0x0b # skyblue.
    FOREGROUND_RED = 0x0c # red.
    FOREGROUND_PINK = 0x0d # pink.
    FOREGROUND_YELLOW = 0x0e # yellow.
    FOREGROUND_WHITE = 0x0f # white.
    
    std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
    
    def set_cmd_text_color(color, handle=std_out_handle):
        Bool = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, color)
        return Bool
    
    def resetColor():
        set_cmd_text_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
    
    def 打印_橙(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_DARKYELLOW)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_黑(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黑字)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_红(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_RED)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_绿(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_GREEN)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_黄(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_YELLOW)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_蓝(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_BLUE)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_紫(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_PINK)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_青(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_SKYBLUE)
            sys.stdout.write(TEXT+'\n')
            resetColor()
    def 打印_白(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(FOREGROUND_WHITE)
            sys.stdout.write(TEXT+'\n')
            resetColor()
elif os.name == 'posix':  # Linux
    
    def 打印_黑(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;30;1m{TEXT}\033[0m")
    
    def 打印_红(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;31;1m{TEXT}\033[0m")
    
    def 打印_绿(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;32;1m{TEXT}\033[0m")
    
    def 打印_黄(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;33;1m{TEXT}\033[0m")
    
    def 打印_蓝(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;34;1m{TEXT}\033[0m")
    
    def 打印_紫(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;35;1m{TEXT}\033[0m")
    
    def 打印_青(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;36;1m{TEXT}\033[0m")
    
    def 打印_白(TEXT, SHOW=1):
        if SHOW == 1:
            print(f"\033[0;37;1m{TEXT}\033[0m")


import struct

## 全局字典变量,方便快速转换字符串掩码和掩码长度
## 点分十进制字符串掩码格式对应掩码位数
D_MASK_STR_2_INT = {
    '0.0.0.0':0, '128.0.0.0':1, '192.0.0.0':2, '224.0.0.0':3, '240.0.0.0':4, '248.0.0.0':5, '252.0.0.0':6, '254.0.0.0':7,
    '255.0.0.0':8, '255.128.0.0':9, '255.192.0.0':10, '255.224.0.0':11, '255.240.0.0':12, '255.248.0.0':13, '255.252.0.0':14, '255.254.0.0':15,
    '255.255.0.0':16, '255.255.128.0':17, '255.255.192.0':18, '255.255.224.0':19, '255.255.240.0':20, '255.255.248.0':21, '255.255.252.0':22, '255.255.254.0':23,
    '255.255.255.0':24, '255.255.255.128':25, '255.255.255.192':26, '255.255.255.224':27, '255.255.255.240':28, '255.255.255.248':29, '255.255.255.252':30, '255.255.255.254':31,
    '255.255.255.255':32}

## 掩码位数对应点分十进制字符串掩码格式
D_MASK_INT_2_STR = {
    0: '0.0.0.0', 1: '128.0.0.0', 2: '192.0.0.0', 3: '224.0.0.0', 4: '240.0.0.0', 5: '248.0.0.0', 6: '252.0.0.0', 7: '254.0.0.0', 
    8: '255.0.0.0', 9: '255.128.0.0', 10: '255.192.0.0', 11: '255.224.0.0', 12: '255.240.0.0', 13: '255.248.0.0', 14: '255.252.0.0', 15: '255.254.0.0', 
    16: '255.255.0.0', 17: '255.255.128.0', 18: '255.255.192.0', 19: '255.255.224.0', 20: '255.255.240.0', 21: '255.255.248.0', 22: '255.255.252.0', 23: '255.255.254.0', 
    24: '255.255.255.0', 25: '255.255.255.128', 26: '255.255.255.192', 27: '255.255.255.224', 28: '255.255.255.240', 29: '255.255.255.248', 30: '255.255.255.252', 31: '255.255.255.254', 
    32: '255.255.255.255'}

## IP_INT 转 IP_STR
## IP_INT(4字节无符号整数类型IP地址)
## IP_STR(点分十进制字符串类型IP地址)
def IP_INT_2_IP_STR(IP_INT):
    IP_Byte = struct.pack('>I', IP_INT)                     # 打包成4字节无符号整数(大端/网络端)
    元组_IP_数字 = struct.unpack('BBBB', IP_Byte)
    ## 每个数字转成字符串并加上'.'拼成点分十形式字符串
    #IP_STR = str(元组_IP_数字[0]) +'.'+ str(元组_IP_数字[1]) +'.'+ str(元组_IP_数字[2]) +'.'+ str(元组_IP_数字[3])
    IP_STR = f'{str(元组_IP_数字[0])}.{str(元组_IP_数字[1])}.{str(元组_IP_数字[2])}.{str(元组_IP_数字[3])}'         ## Python3可用的新写法
    return(IP_STR)

## IP_STR 转 IP_INT
## IP_STR(点分十进制字符串类型IP地址)
## IP_INT(4字节无符号整数类型IP地址)
def IP_STR_2_IP_INT(IP_STR):
    IP分段 = IP_STR.split('.')
    B_IP = struct.pack('BBBB', int(IP分段[0]), int(IP分段[1]), int(IP分段[2]), int(IP分段[3]))  # 4个字节数字按顺序拼成4字节字节码
    T_IP_INT = struct.unpack('>I', B_IP)    # 把拼成的4字节字节码转成大端格式的4字节无符号整数
    IP_INT = T_IP_INT[0]                    # 打包成字节返回是元组,第一个元素是打包的结果
    return(IP_INT)

## 测试掩码格式,并转成掩码位数
## MASK 参数类型:字符串('24' 或 '255.255.255.0' 样式)
## MASK 参数类型:数字(0到32)
## 成功返回 (0, 掩码位数(数字))
## 失败返回 (1, 错误信息)
def TEST_MASK(MASK):
    if type(MASK) == int:                               ## 如果是数字类型,后续再判断数值是否在掩码范围内
        if 0 <= MASK <= 32:                             ## 如果掩码数值在0到32内
            return(0, MASK)                             ## 返回成功状态码0和掩码数值
        else:
            ERROR = 'ERROR 变量范围:掩码只能在0到32范围内'
            return(1, ERROR)
    elif type(MASK) == str:                             ## 如果是字符串类型,可能是数字掩码,可能是字符串类型的数字掩码
        try:
            ## 尝试把参数MASK转成数字类型
            MASK_INT = int(MASK)                        ## int() 函数可以自动删除字符串类型数字的前后空格
        except:
            ## 不能转换成数字可能是点分十格式掩码的字符串
            L_MASK = MASK.split('.')                    ## 以点分割,各元素组成列表
            if len(L_MASK) == 4:                        ## 如果能分成4段,说明是点分十格式掩码字符串
                if MASK in D_MASK_STR_2_INT:            ## 如果这个点分十格式掩码字符串存在于事先做好的掩码和掩码位数字典中
                    MASK_INT = D_MASK_STR_2_INT[MASK]   ## 获取点分十格式掩码字符串对应的掩码数值
                    return(0, MASK_INT)                 ## 返回成功状态码0和掩码数值
                else:
                    ERROR = 'ERROR 变量范围:掩码超出有效范围'
                    return(1, ERROR)
            else:                                       ## 如果不能分成4段,格式错误
                ERROR = 'ERROR 变量格式:掩码格式错误'
                return(1, ERROR)
        else:                                           ## 可以转成数字
            R = TEST_MASK(MASK_INT)                     ## 调用自身函数处理新的数字类型参数
            return(R)                                   ## 返回自身处理结果给上级函数
    else:
        ERROR = 'ERROR 参数类型:只能是数字或字符串或字符串类型的数字'
        return(1, ERROR)

## 测试IP格式
## MASK 参数类型:字符串('192.168.0.1' 样式)
## 成功返回 (0, 正确字符串IP格式)
## 失败返回 (1, 错误信息)
def TEST_IP(IP):
    if type(IP) == str:
        L_IP = IP.split('.')                    ## 以点分割,各元素组成列表
        if len(L_IP) == 4:
            try:                                ## 尝试把4段内容转成数字
                IP_0 = int(L_IP[0])
                IP_1 = int(L_IP[1])
                IP_2 = int(L_IP[2])
                IP_3 = int(L_IP[3])
            except:
                ERROR = 'ERROR 参数格式(字符串):以点分成4段中有不能转成数字的部分'
                return(1, ERROR)
            else:                               ## 4段值各自转成数字成功后判断每个数字的取值范围
                if 0<= IP_0 <=255 and 0<= IP_1 <=255 and 0<= IP_2 <=255 and 0<= IP_3 <=255:
                    return(0, IP)
                else:
                    ERROR = 'ERROR 参数范围:以点分成4段中有超出0-255范围的值'
                    return(1, ERROR)
        else:
            ERROR = 'ERROR 参数格式(字符串):不能以点分成4段'
            return(1, ERROR)
    else:
        ERROR = 'ERROR 参数类型:不是字符串'
        return(1, ERROR)


## 根据地址和掩码计算起始和结束地址
## 返回(NET_IP_MIN, NET_IP_MAX)
## NET_IP_MIN(网段首地址/网段号)
## NET_IP_MAX(网段末地址/广播号)
def 计算网络区间_IP_STR(IP_STR, MASK_INT):
    主机位数 = 32 - MASK_INT
    主机数量 = 2**主机位数
    IP_INT = IP_STR_2_IP_INT(IP_STR)
    位移操作缓存 = IP_INT >> 主机位数
    IP_NET_INT = 位移操作缓存 << 主机位数
    NET_IP_MIN = IP_NET_INT                 ## 每个网段的首IP地址为网段号
    NET_IP_MAX = IP_NET_INT + 主机数量 -1   ## 每个网段的末IP地址为广播号(以0为第一个,所以最后一个要总数-1)
    return(NET_IP_MIN, NET_IP_MAX)

def 计算网络区间(IP_INT, MASK_INT):
    主机位数 = 32 - MASK_INT
    主机数量 = 2**主机位数
    位移操作缓存 = IP_INT >> 主机位数
    IP_NET_INT = 位移操作缓存 << 主机位数
    NET_IP_MIN = IP_NET_INT                 ## 每个网段的首IP地址为网段号
    NET_IP_MAX = IP_NET_INT + 主机数量 -1   ## 每个网段的末IP地址为广播号(以0为第一个,所以最后一个要总数-1)
    return(NET_IP_MIN, NET_IP_MAX)

## 传入IP数字类型和掩码位数,返回网段号数字类型
def IP_INT_2_NET_INT(IP_INT, MASK_INT):
    主机位数 = 32 - MASK_INT
    位移操作缓存 = IP_INT >> 主机位数
    NET_INT = 位移操作缓存 << 主机位数
    return(NET_INT)



## 字符串格式IP列表转成各掩码下网络号(32位掩码IP的网段号就是本身),通过集合去除重复值
## L_IP_NET    = ['192.168.0.1/32', '192.168.0.1', '192.168.0.3/30', '192.168.0.5/255.255.255.0']
## D_MASK_NETs = {32:set(网段号1, 网段号2), 31:set(), ...}
def L_IP_STR_2_D_MASK_NETs(L_IP_NET, 掩码识别模式, SHOW=0):
    ## 补全 D_MASK_NETs 中缺失掩码,方便后续操作
    D_MASK_NETs = {}
    打印_黄("初始化操作: D_MASK_NETs 初始化掩码对应空集合", SHOW)
    for MASK_INT in range(0, 33):
        D_MASK_NETs[MASK_INT] = set()
    打印_紫(f"  初始化 D_MASK_NETs={D_MASK_NETs}", SHOW)
    
    打印_黄("IP或NET的列表转成掩码字典,值为掩码对应网络号集合", SHOW)
    for IP_STR in L_IP_NET:
        IP = ''
        MASK = ''
        SP_IP_STR = IP_STR.split('/')                                   # 拆分IP和掩码
        if len(SP_IP_STR) == 1:
            ## 无掩码信息,只有一个IP字符串
            R_IP = TEST_IP(SP_IP_STR[0])
            if R_IP[0] != 0:
                打印_红(f"  IP_STR={IP_STR:18s} SP_IP_STR={SP_IP_STR} IP格式异常: {R_IP}", SHOW)
                continue
            IP_INT = IP_STR_2_IP_INT(R_IP[1])               # IP字符转IP数字
            MASK_INT = 32                                   # 无掩码默认为32位掩码
            NET_ID_INT = IP_INT                             # 32位掩码,单个地址作为一个网段,网络号=地址本身
            D_MASK_NETs[MASK_INT].add(NET_ID_INT)           # 添加到集合,利用集合去重
            打印_绿(f"  {IP_STR:18s} 无掩码信息当单个地址处理  (网络号){IP_INT_2_IP_STR(NET_ID_INT)} D_MASK_NETs[{MASK_INT}].add({NET_ID_INT})", SHOW)
        elif len(SP_IP_STR) == 2:
            ## 有掩码信息
            R_IP = TEST_IP(SP_IP_STR[0])
            if R_IP[0] != 0:
                打印_红(f"  IP_STR={IP_STR:18s} SP_IP_STR={SP_IP_STR} IP格式异常: {R_IP}", SHOW)
                continue
            R_MASK = TEST_MASK(SP_IP_STR[1])
            if R_MASK[0] != 0:
                打印_红(f"  IP_STR={IP_STR:18s} SP_IP_STR={SP_IP_STR} MASK格式异常: {R_IP}", SHOW)
                continue
            IP_INT = IP_STR_2_IP_INT(R_IP[1])
            MASK_INT = R_MASK[1]
            NET_ID_INT = IP_INT_2_NET_INT(IP_INT, MASK_INT)
            ## 掩码识别模式
            if 掩码识别模式 == '全当网段':
                D_MASK_NETs[MASK_INT].add(NET_ID_INT)
                打印_绿(f"  {IP_STR:18s} {R_IP[1]}(地址)  {IP_INT_2_IP_STR(NET_ID_INT)}(网络号) 掩码识别模式={掩码识别模式} 当网段处理 D_MASK_NETs[{MASK_INT}].add({NET_ID_INT})", SHOW)
            elif 掩码识别模式 == '全当地址':
                D_MASK_NETs[32].add(IP_INT)
                打印_绿(f"  {IP_STR:18s} {R_IP[1]}(地址)  {IP_INT_2_IP_STR(NET_ID_INT)}(网络号) 掩码识别模式={掩码识别模式} 当地址处理 D_MASK_NETs[32].add({IP_INT})", SHOW)
            else:
                掩码识别模式='常规'     ## 默认掩码识别模式='常规'
                if NET_ID_INT == IP_INT:
                    D_MASK_NETs[MASK_INT].add(NET_ID_INT)
                    打印_绿(f"  {IP_STR:18s} {R_IP[1]}(地址)==(网络号){IP_INT_2_IP_STR(NET_ID_INT)} 掩码识别模式={掩码识别模式} 当网段处理 D_MASK_NETs[{MASK_INT}].add({NET_ID_INT})", SHOW)
                else:
                    D_MASK_NETs[32].add(IP_INT)
                    打印_绿(f"  {IP_STR:18s} {R_IP[1]}(地址)!=(网络号){IP_INT_2_IP_STR(NET_ID_INT)} 掩码识别模式={掩码识别模式} 当地址处理 D_MASK_NETs[32].add({IP_INT})", SHOW)
        else:
            打印_红(f"  IP_STR={IP_STR:18s} SP_IP_STR={SP_IP_STR} IP格式异常: 大于2段", SHOW)
    打印_紫(f"  填入值 D_MASK_NETs={D_MASK_NETs}", SHOW)
    return(D_MASK_NETs)

## 相邻网段合并
def D_MASK_NETs_合并网段(D_MASK_NETs, SHOW=0):
    L_合并网段信息 = []
    打印_黄("合并网段开始操作: 遍历 D_MASK_NETs 中掩码(从大到小 32->0)", SHOW)
    MASK_INT = 33
    while 1:
        MASK_INT -= 1
        if MASK_INT == 0:
            打印_红("    掩码=0 掩码为0,终止", SHOW)
            break
        if D_MASK_NETs[MASK_INT] == set():
            打印_白(f"    掩码={MASK_INT} 不存在掩码为{MASK_INT}的网段,跳过", SHOW)
            continue
        
        ## 同掩码且网络号相同的放一起
        D_NET_IP_CACHE = {}                     # 临时存储各网段号对应IP,样式 {网络号1:[IP1, IP2], 网络号2:[ip3,ip4]}
        for IP_INT in D_MASK_NETs[MASK_INT]:    # 提取当前掩码相关IP
            扩一级网络号 = IP_INT_2_NET_INT(IP_INT, MASK_INT-1)   # 计算IP掩码-1时的网段号
            打印_红(f"    掩码={MASK_INT} {IP_INT:10}/{MASK_INT} ({IP_INT_2_IP_STR(IP_INT):>15s}/{MASK_INT:2})  扩大一级: {扩一级网络号:10}/{MASK_INT-1} ({IP_INT_2_IP_STR(扩一级网络号):>15s}/{MASK_INT-1}) 添加到临时 D_NET_IP_CACHE[{扩一级网络号}].add({IP_INT}) 中", SHOW)
            #广播号 = (32-MASK_INT)*2 + 1 + 扩一级网络号  # 32-0 31-1 30-3 29-7 28-15
            if 扩一级网络号 in D_NET_IP_CACHE:           # 
                D_NET_IP_CACHE[扩一级网络号].add(IP_INT)
            else:
                D_NET_IP_CACHE[扩一级网络号] = set()
                D_NET_IP_CACHE[扩一级网络号].add(IP_INT)
        
        打印_黄(f"    掩码={MASK_INT} 的IP(扩一级掩码)={MASK_INT-1} 后的组网情况 D_NET_IP_CACHE: {D_NET_IP_CACHE}", SHOW)
        
        ## 检查是否可以合并,在此处大一级的网段包含2个小网段就可以合并成一个大网段
        for NET_ID in D_NET_IP_CACHE:
            if len(D_NET_IP_CACHE[NET_ID]) == 2:
                打印_绿(f"        可以合并 {NET_ID}:{D_NET_IP_CACHE[NET_ID]} # 字符展示: {IP_INT_2_IP_STR(NET_ID)}/{MASK_INT-1} : {[IP_INT_2_IP_STR(i)+'/'+str(MASK_INT) for i in D_NET_IP_CACHE[NET_ID]]}", SHOW)
                打印_蓝(f"            在D_NET[{MASK_INT-1}]中[添加] {NET_ID}/{MASK_INT-1} {IP_INT_2_IP_STR(NET_ID)}/{MASK_INT-1} 合并后的网段号添加到大一级的网段集合中", SHOW)
                #打印_橙(f"合并网段 {' + '.join([IP_INT_2_IP_STR(i)+'/'+str(MASK_INT) for i in D_NET_IP_CACHE[NET_ID]])} = {IP_INT_2_IP_STR(NET_ID)}/{MASK_INT-1}", SHOW=1)
                L_合并网段信息.append(([(i, MASK_INT) for i in D_NET_IP_CACHE[NET_ID]], (NET_ID, MASK_INT-1)))
                D_MASK_NETs[MASK_INT-1].add(NET_ID)     # 合并的新网段信息保存到 D_MASK_NETs
                for i in D_NET_IP_CACHE[NET_ID]:
                    打印_蓝(f"            从D_NET[{MASK_INT}]中[删除] {i}/{MASK_INT} {IP_INT_2_IP_STR(i)}/{MASK_INT}", SHOW)
                    D_MASK_NETs[MASK_INT].discard(i)     # 可以合并的从指定掩码删除
            else:
                打印_青(f"        不能合并 {NET_ID}:{D_NET_IP_CACHE[NET_ID]}             # 字符展示: {IP_INT_2_IP_STR(NET_ID)}/{MASK_INT-1} : {[IP_INT_2_IP_STR(i)+'/'+str(MASK_INT) for i in D_NET_IP_CACHE[NET_ID]]}", SHOW)
                for i in D_NET_IP_CACHE[NET_ID]:
                    打印_蓝(f"            在D_NET[{MASK_INT}]中[维持] {i}/{MASK_INT} {IP_INT_2_IP_STR(i)}/{MASK_INT} 的状态", SHOW)
    打印_紫(f"  合并后 D_MASK_NETs={D_MASK_NETs}", SHOW)
    return(L_合并网段信息)

## 删除包含关系网段
def D_MASK_NETs_删除包含关系网段(D_MASK_NETs, SHOW=0):
    L_包含网段信息 = []
    打印_黄("D_MASK_NETs 删除包含关系网段", SHOW)
    ## 遍历 D_MASK_NETs 记录有网段的掩码号
    L_MASK_INT = []
    for MASK_INT in range(0,33):
        if MASK_INT in D_MASK_NETs and D_MASK_NETs[MASK_INT] != set():
            L_MASK_INT.append(MASK_INT)
    
    打印_黄(f"有网段记录的掩码号列表: {L_MASK_INT}", SHOW)
    if len(L_MASK_INT) > 1:     ## 网段不重复时,不同掩码网段才有可能有包含关系
        L_不重复不包含网段 = [计算网络区间(NET_ID, L_MASK_INT[0]) for NET_ID in D_MASK_NETs[L_MASK_INT[0]]]  # 根据掩码和网络号转成网络号区间值,用于对比
        ## 从第二大网段开始遍历查找被包含网段
        L_NET_DEL = []  # 记录被包含的小网段信息
        for MASK_INT in L_MASK_INT[1:]:
            L_NET_ID = D_MASK_NETs[MASK_INT]
            打印_黄(f"    掩码: {MASK_INT} L_NET_ID: {L_NET_ID}", SHOW)
            for NET_ID in L_NET_ID:
                IP_INT_MIN, IP_INT_MAX = 计算网络区间(NET_ID, MASK_INT)
                打印_白(f"        遍历 {NET_ID}/{MASK_INT} {IP_INT_2_IP_STR(NET_ID)}/{MASK_INT} ({IP_INT_2_IP_STR(IP_INT_MIN)} -> {IP_INT_2_IP_STR(IP_INT_MAX)})", SHOW)
                打印_蓝(f"            对比 L_不重复不包含网段={L_不重复不包含网段} # 字符展示: {[(IP_INT_2_IP_STR(i),IP_INT_2_IP_STR(j)) for i,j in L_不重复不包含网段]}", SHOW)
                被包含标识 = 0
                for NET_UP in L_不重复不包含网段:
                    if IP_INT_MIN >= NET_UP[0] and IP_INT_MAX <= NET_UP[1]:
                        被包含标识 = 1
                        break
                if 被包含标识 == 1:
                    L_NET_DEL.append((MASK_INT, NET_ID))
                    打印_绿(f"                被 ({IP_INT_2_IP_STR(NET_UP[0])} {IP_INT_2_IP_STR(NET_UP[1])}) 包含 记录到删除列表 L_NET_DEL.append({(MASK_INT, NET_ID)}) # 可删除: {IP_INT_2_IP_STR(NET_ID)}/{MASK_INT}", SHOW)
                    #打印_橙(f"包含网段 {IP_INT_2_IP_STR(NET_ID)}/{MASK_INT} < ({IP_INT_2_IP_STR(NET_UP[0])} {IP_INT_2_IP_STR(NET_UP[1])})", SHOW=1)
                    L_包含网段信息.append(((NET_ID,MASK_INT), NET_UP))
                else:
                    L_不重复不包含网段.append(计算网络区间(NET_ID, MASK_INT))
                    打印_青(f"                找不到包含关系 L_不重复不包含网段.append({计算网络区间(NET_ID, MASK_INT)})", SHOW)
        打印_黄(f"删除被包含网段 L_NET_DEL={L_NET_DEL}", SHOW)
        for MASK_INT, NET_ID in L_NET_DEL:
            D_MASK_NETs[MASK_INT].remove(NET_ID)
    else:
        打印_黄("网段之间没有包含关系", SHOW)
    
    打印_紫(f"  删包含 D_MASK_NETs={D_MASK_NETs}", SHOW)
    return(L_包含网段信息)


def 合并IPv4地址(L_IP_NET, 掩码识别模式, SHOW):
    L_NET = []
    L_IP  = []
    
    print(f"源地址数量={len(L_IP_NET)}")
    print(f"去重后数量={len(set(L_IP_NET))}")
    
    D_MASK_NETs = L_IP_STR_2_D_MASK_NETs(L_IP_NET, 掩码识别模式, SHOW)    # IP或NET的列表转成掩码字典,值为掩码对应网络号集合
    L_合并网段信息 = D_MASK_NETs_合并网段(D_MASK_NETs, SHOW)              # 相邻网段尝试合并
    L_包含网段信息 = D_MASK_NETs_删除包含关系网段(D_MASK_NETs, SHOW)      # 删除包含关系网段
    
    ## 合并网段信息
    for L, BIG_NET in L_合并网段信息:
        打印_青(f"合并网段 {' + '.join([IP_INT_2_IP_STR(IP_INT)+'/'+str(MASK_INT) for IP_INT,MASK_INT in L])} = {IP_INT_2_IP_STR(BIG_NET[0])}/{BIG_NET[1]}", SHOW=1)
        #print(f"  source-address {IP_INT_2_IP_STR(BIG_NET[0])} {BIG_NET[1]}")
        #print(f"  undo source-address {IP_INT_2_IP_STR(L[0][0])} {L[0][1]}")
        #print(f"  undo source-address {IP_INT_2_IP_STR(L[1][0])} {L[1][1]}")
    
    ## 包含网段信息
    for 小网段, 大网段 in L_包含网段信息:
        打印_绿(f"包含网段 {IP_INT_2_IP_STR(小网段[0])}/{小网段[1]} < ({IP_INT_2_IP_STR(大网段[0])} {IP_INT_2_IP_STR(大网段[1])})", SHOW=1)
        #print(f"  undo source-address {IP_INT_2_IP_STR(小网段[0])} {小网段[1]}")
    
    ## 删除空值
    #for MASK_INT in range(0,33):
    #    if MASK_INT in D_MASK_NETs and D_MASK_NETs[MASK_INT] == set():
    #        del D_MASK_NETs[MASK_INT]
    #print(D_MASK_NETs)
    
    ## 区分网段和IP
    for MASK_INT in D_MASK_NETs:
        if MASK_INT == 32:
            for IP in D_MASK_NETs[MASK_INT]:
                L_IP.append(IP_INT_2_IP_STR(IP))
        else:
            for NET_ID in D_MASK_NETs[MASK_INT]:
                L_NET.append(f"{IP_INT_2_IP_STR(NET_ID)}/{MASK_INT}")
    return(L_NET, L_IP)



if __name__ == '__main__':
    
    ## 测试
    L_IP_NET = []
    L_IP_NET = ['114.114.114.114']
    L_IP_NET = ['0.0.0.0/0', '10.10.10.10/8']           # 包含所有网段
    L_IP_NET = ['128.0.0.0/1', '0.0.0.0/1', '1.2.3.4']  # 合并后包含所有网段
    L_IP_NET = ['192.168.0.0', '192.168.0.0/32', '192.168.0.1/255.255.255.0', '192.168.0.2', '192.168.0.3', '192.168.0.4']
    L_IP_NET = ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8']
    L_IP_NET = ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8', '192.168.1.100/24', '172.16.30.2']
    
    ## 此脚本判断没有掩码信息的地址时,都被视作一个IP,如:192.168.0.0 被视作一个IP 192.168.0.0/32,不视作网段 192.168.0.0/16
    ## 掩码识别模式:当有掩码,掩码前的地址是否是网段号时,根据设置判断这是一个网段还是网段中的一个地址
    掩码识别模式 = '常规'     # 192.168.1.0/24 表示192.168.1.0-192.168.1.255整个网段,192.168.1.1/24 表示是网段中的1个地址192.168.1.1
    #掩码识别模式 = '全当网段' # 192.168.1.0/24 和 192.168.1.1/24 都表示192.168.1.0-192.168.1.255整个网段
    #掩码识别模式 = '全当地址' # 192.168.1.0/24 和 192.168.1.1/24 都表示1个地址
    
    L_NET, L_IP = 合并IPv4地址(L_IP_NET, 掩码识别模式, SHOW=1)
    print("IP ", L_IP)
    print("NET", L_NET)
    print(f"IP数量={len(L_IP)} 网段数量={len(L_NET)} 合计={len(L_IP)+len(L_NET)}")
    

 类似资料: