IP地址IP网段合并

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

import os
## 终端显示颜色
if os.name == 'nt':       # Windows
    import ctypes,sys
    STD_OUTPUT_HANDLE = -11
    
    # Windows CMD命令行 字体颜色定义 text colors
    黑字 = 0x00 # black.
    暗蓝字 = 0x01 # dark blue.
    暗绿字 = 0x02 # dark green.
    暗青字 = 0x03 # dark skyblue.
    暗红字 = 0x04 # dark red.
    暗紫字 = 0x05 # dark pink.
    暗黄字 = 0x06 # dark yellow.
    暗白字 = 0x07 # dark white.
    灰字 = 0x08 # dark gray.
    蓝字 = 0x09 # blue.
    绿字 = 0x0a # green.
    青字 = 0x0b # skyblue.
    红字 = 0x0c # red.
    紫字 = 0x0d # pink.
    黄字 = 0x0e # yellow.
    白字 = 0x0f # white.
    
    # Windows CMD命令行 背景颜色定义 background colors
    暗蓝底 = 0x10 # dark blue.
    暗绿底 = 0x20 # dark green.
    暗青底 = 0x30 # dark skyblue.
    暗红底 = 0x40 # dark red.
    暗紫底 = 0x50 # dark pink.
    暗黄底 = 0x60 # dark yellow.
    暗白底 = 0x70 # dark white.
    灰底 = 0x80 # dark gray.
    蓝底 = 0x90 # blue.
    绿底 = 0xa0 # green.
    青底 = 0xb0 # skyblue.
    红底 = 0xc0 # red.
    紫底 = 0xd0 # pink.
    黄底 = 0xe0 # yellow.
    白底 = 0xf0 # 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(红字 | 绿字 | 蓝字)
    
    def 打印_黑(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黑字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_灰(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(灰字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_蓝(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(蓝字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_绿(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(绿字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_青(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(青字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_红(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(红字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_紫(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(紫字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_黄(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黄字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(白字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()

    def 打印_暗蓝(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(暗蓝字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_暗绿(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(暗绿字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_暗青(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(暗青字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_暗红(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(暗红字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_暗紫(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(暗紫字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_暗黄(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(暗黄字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_暗白(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(暗白字)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()

    def 打印_白底黑字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黑字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底灰字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(灰字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底红字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(红字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底绿字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(绿字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底黄字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黄字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底蓝字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(蓝字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底紫字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(紫字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_白底青字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(青字 | 白底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    
    def 打印_蓝底黄字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(黄字 | 蓝底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
    def 打印_蓝底白字(TEXT, SHOW=1):
        if SHOW == 1:
            set_cmd_text_color(白字 | 蓝底)
            sys.stdout.write(f"{TEXT}\n")
            resetColor()
elif os.name == 'posix':  # Linux
    '''
    格式: print('\033[显示方式;前景颜色;背景颜色m ..........\033[0m')
    print('\033[31;42m 123\033[0m')
    
    显示方式
        0 默认
        1 高亮显示
        4 下划线
        5 闪烁
        7 反白显示
        8 不可见
    
    颜色 前景色 背景色
    黑色     30     40
    红色     31     41
    绿色     32     42
    黄色     33     43
    蓝色     34     44
    紫色     35     45
    青色     36     46
    白色     37     47
    '''
    
    def 打印_灰(TEXT):
        print(f"\033[0;30;1m{TEXT}\033[0m")
    def 打印_红(TEXT):
        print(f"\033[0;31;1m{TEXT}\033[0m")
    def 打印_绿(TEXT):
        print(f"\033[0;32;1m{TEXT}\033[0m")
    def 打印_黄(TEXT):
        print(f"\033[0;33;1m{TEXT}\033[0m")
    def 打印_蓝(TEXT):
        print(f"\033[0;34;1m{TEXT}\033[0m")
    def 打印_紫(TEXT):
        print(f"\033[0;35;1m{TEXT}\033[0m")
    def 打印_青(TEXT):
        print(f"\033[0;36;1m{TEXT}\033[0m")
    def 打印_白(TEXT):
        print(f"\033[0;37;1m{TEXT}\033[0m")
    
    def 打印_白底黑字(TEXT):
        print(f"\033[0;30;47m{TEXT}\033[0m")
    def 打印_白底红字(TEXT):
        print(f"\033[0;31;47m{TEXT}\033[0m")
    def 打印_白底绿字(TEXT):
        print(f"\033[0;32;47m{TEXT}\033[0m")
    def 打印_白底黄字(TEXT):
        print(f"\033[0;33;47m{TEXT}\033[0m")
    def 打印_白底蓝字(TEXT):
        print(f"\033[0;34;47m{TEXT}\033[0m")
    def 打印_白底紫字(TEXT):
        print(f"\033[0;35;47m{TEXT}\033[0m")
    def 打印_白底青字(TEXT):
        print(f"\033[0;36;47m{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)
    
    L_NET_DEL = []  # 记录被包含的小网段信息
    P_被包含网段 = set()
    LEN_MASK = len(L_MASK_INT)
    for BN in range(0, LEN_MASK):
        打印_白(f"遍历 BN={BN}({L_MASK_INT[BN]})")
        for SN in range(BN+1, LEN_MASK):
            打印_白(f"  对比 SN={SN}({L_MASK_INT[SN]})", SHOW)
            MASK_B = L_MASK_INT[BN]
            MASK_S = L_MASK_INT[SN]
            for NET_ID_B in D_MASK_NETs[MASK_B]:
                NET_B_区间 = 计算网络区间(NET_ID_B, MASK_B)
                打印_青(f"    NET_ID_B={NET_ID_B}({IP_INT_2_IP_STR(NET_ID_B)}/{MASK_B}) {NET_B_区间}", SHOW)
                if (MASK_B, NET_ID_B) in P_被包含网段:
                    打印_灰("      忽略(已被其他网段包含)", SHOW)
                else:
                    for NET_ID_S in D_MASK_NETs[MASK_S]:
                        NET_S_区间 = 计算网络区间(NET_ID_S, MASK_S)
                        打印_绿(f"      NET_ID_S={NET_ID_S}({IP_INT_2_IP_STR(NET_ID_S)}/{MASK_S}) {NET_S_区间}", SHOW)
                        if (MASK_S, NET_ID_S) in P_被包含网段:
                            打印_灰("        忽略(已被其他网段包含)", SHOW)
                        else:
                            if NET_S_区间[0] > NET_B_区间[1] or NET_S_区间[1] < NET_B_区间[0]:
                                打印_黄("        保留", SHOW)
                            else:
                                打印_灰("        删除(被上级网段包含)", SHOW)
                                L_包含网段信息.append(((NET_ID_S,MASK_S), NET_B_区间))
                                L_NET_DEL.append((MASK_S, NET_ID_S))
                                P_被包含网段.add((MASK_S, NET_ID_S))
    打印_黄(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)
    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)}")
    

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值