【问题标题】:Python - Best way to count IPs that fall under IP rangePython - 计算 IP 范围内 IP 的最佳方法
【发布时间】:2019-05-20 12:07:19
【问题描述】:

我有一个 python 脚本,它从 arp 表中获取所有 IP 并将其分配给一个变量。我有 for 循环创建另外两个变量 start_IP 包含子网的第一个 IP 和 last_IP 包含同一子网中的最后一个 IP。对于每个循环,我将有一个不同的开始和最后一个 IP。

我正在尝试检查包含所有 IP 的变量,并查看每个子网中有多少 IP。

最好的方法是什么?这是一个硬编码的示例: 计数 = 0

arps = ['10.20.30.130','10.20.30.131','10.20.30.132', '10.20.30.133', 
'10.20.30.136', '10.20.30.137', '10.20.30.138', '10.20.30.139', '10.20.30.140', '10.20.30.141', '10.20.30.143', '10.20.30.149']
 start_ip = "10.20.30.132"
 end_ip = "10.20.30.142"
 count = 0      
 for arp in arps:
    if arp >= start_ip and arp <= end_ip:
        count = count + 1
        print count
    else:
        continue

 print "Count: ", count

有没有更好更快的方法来做到这一点?

【问题讨论】:

    标签: python python-2.7 ip


    【解决方案1】:

    两种方式。简单的方法:

    IP 地址逐个八位字节进行比较。有趣的是,Python 列表逐个元素进行比较。因此,如果您只是将 IP 地址按点拆分并将列表映射到int,您可以正确比较它们。

    更简单的方法:

    ipaddress.ip_address 是可比较的,只要比较的地址是相同的版本(IPv4 或 IPv6)。

    但是,字符串比较不能提供正确的 IP 地址顺序:

    '1.12.1.1' < '1.2.1.1'
    # => True (should be False)
    

    除了这些问题,您的代码都很好。可以写得更简洁:

    import ipaddress
    arps = ['10.20.30.130','10.20.30.131','10.20.30.132', '10.20.30.133', 
        '10.20.30.136', '10.20.30.137', '10.20.30.138', '10.20.30.139', 
        '10.20.30.140', '10.20.30.141', '10.20.30.143', '10.20.30.149']
    start_ip = "10.20.30.132"
    end_ip = "10.20.30.142"
    
    start_ip_ip = ipaddress.ip_address(start_ip)
    end_ip_ip = ipaddress.ip_address(end_ip)
    
    sum(1 for ip in arps if start_ip_ip <= ipaddress.ip_address(ip) <= end_ip_ip)
    # => 8
    

    如果你特别想查看特定子网中的地址,你甚至不需要使用开始和结束地址,如果你知道子网规范:

    ipaddress.ip_address('192.168.1.17') in ipaddress.ip_network('192.168.0.0/16')
    # => True
    

    【讨论】:

    • 我的数据库中确实有 CIDR,因此您的最后一个解决方案可能会完美运行并最大限度地减少代码。我会试一下!谢谢
    • 我将使用第一个解决方案。我使用的是 python 2,所以我必须先将所有 IP 字符串转换为 Unicode 才能使其工作。
    【解决方案2】:

    我可以想到以下两种解决方案。方法一的时间复杂度为O(N),方法二的时间复杂度为O(Nlog N)。正如 Amadan 所建议的,IP 地址需要事先进行预处理。

    import bisect
    
    arps = ['10.20.30.130','10.20.30.131','10.20.30.132', '10.20.30.133', 
    '10.20.30.136', '10.20.30.137', '10.20.30.138', '10.20.30.139', '10.20.30.140', '10.20.30.141', '10.20.30.143', '10.20.30.149']
    start_ip = "10.20.30.132"
    end_ip = "10.20.30.142"
    
    # Padding zeros to the IP addresses to make sure they are directly comparable
    def padding(s):
        return s.zfill(3)
    
    arps = [".".join(list(map(padding, x.split(".")))) for x in arps]
    start_ip = ".".join(list(map(padding, start_ip.split("."))))
    end_ip   = ".".join(list(map(padding, end_ip.split("."))))
    
    # Method 1: Pythonic one-liner
    print(sum(start_ip <= x <= end_ip for x in arps))
    
    # Method 2: Sort and binary search
    def find_lt(a, x):
        i = bisect.bisect_right(a, x)
        if i:
            return i - 1
        else:
            return 0
    
    def find_gt(a, x):
        i = bisect.bisect_right(a, x)
        if i != len(a):
            return i
        else:
            return i
    
    arps.sort()
    print(find_gt(arps, end_ip) - find_lt(arps, start_ip))
    

    【讨论】:

    • 这是不正确的,因为我在回答中解释的问题 - sort 不会按正确的顺序对字符串 IP 进行排序。
    • @Amadan 是的,你是对的。让我展示一个解决方法。
    • 此外,如果您忽略对它们进行排序的事实,方法 2 的复杂度为 O(log N)。排序本身是 O(N log N)。 (当然,big-O 不是衡量速度的标准,只是复杂度……)
    • @Amadan 真的。谢谢。现在怎么样?
    • 它会起作用,虽然我认为list(map(int, x.split(".")))".".join(list(map(padding, x.split(".")))) 更简单、更快。
    猜你喜欢
    • 1970-01-01
    • 2011-08-17
    • 2018-01-04
    • 2016-12-13
    • 2013-11-01
    • 2012-10-06
    • 2012-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多