【问题标题】:Given two files (IP's and Subnet Info), create file that associates each IP to a subnet给定两个文件(IP 和子网信息),创建将每个 IP 关联到子网的文件
【发布时间】:2017-06-21 11:01:53
【问题描述】:

几天来,我一直在努力寻找解决此解决方案的正确方法,我正在寻求一些帮助。

我有两个文件,需要创建第三个来显示关系。

  1. IP 地址文件 - ip.csv
  2. 子网文件 - subnet.csv

我需要指定每个 IP 所在的子网,并创建第三个文件

ip.csv 文件将包含大约 150 万个 IP,subnet.csv 文件将包含大约 140,000 个子网。

ip.csv 文件示例:

IP,Type
10.78.175.167,IPv4
10.20.3.56,IPv4

subnet.csv 文件示例:

Subnet,Netmask
10.176.122.136/30,255.255.255.252
10.20.3.0/24,255.255.254.0

我需要创建的文件格式:

Subnet,IP
10.20.3.0/24,10.20.3.56

我尝试使用这些页面中的内容:

这是我尝试过的代码。它适用于小型集,但我在使用完整文件集运行它时遇到问题。

#!/usr/local/bin/python2.7
import csv
import ipaddress
import iptools
import re
import SubnetTree
import sys
from socket import inet_aton

testdir = '/home/test/testdir/'
iprelfile = testdir + 'relationship.csv'
testipsub = testdir + 'subnet.csv'
testipaddr = testdir + 'ip.csv'

o1 = open (iprelfile, "a")

# Subnet file
IPR = set()
o1.write('Subnet,IP\n')
with open(testipsub, 'rb') as master:
    reader = csv.reader(master)
    for row in reader:
        if 'Subnet' not in row[0]:
            # Convert string to unicode to be parsed with ipaddress module
            b = unicode(row[1])
            # Using ipaddress module to create list containing every IP in subnet
            n2 = ipaddress.ip_network(b)
            b1 = (list(n2.hosts()))
            # IP address file
            with open(testipaddr, 'rb') as ipaddy:
                readera = csv.reader(ipaddy)
                for rowa in readera:
                    if 'IP' not in rowa[0]:
                        bb = rowa[0]
                        for ij in b1:
                            # Convert to string for comparison
                            f = str(ij)
                            # If the IP address is in subnet range
                            if f == bb:
                                IPR.update([row[0] + ',' + bb + '\n'])


for ip in IPR:
    o1.write(ip + '\n')

# Closing the file
o1.close()

【问题讨论】:

  • 什么样的问题?错误或低速?
  • 您应该同时打开前两个 CSV 文件,而不是对第一个文件的每一行都打开第二个
  • 我对 Python 还很陌生。你能帮我交换我的代码,以便我同时打开文件吗?
  • 它真的很慢,没有错误。如果我做一个小样本,效果很好。
  • 可能先读取所有子网,然后再读取 IP 并检查每个子网。您可以使用字典(或二叉树)来搜索您必须检查的子网。

标签: python csv ip-address subnet netmask


【解决方案1】:

您可以将所有子网读入内存并按网络地址对其进行排序。这将允许您使用bisect 进行二进制搜索,以便找到每个 IP 的子网。这仅适用于子网不相互重叠的情况,如果重叠,您可能需要使用segment tree

import bisect
import csv
import ipaddress

def sanitize(ip):
    parts = ip.split('/', 1)
    parts[0] = '.'.join(str(int(x)) for x in parts[0].split('.'))

    return '/'.join(parts)

with open('subnet.csv') as subnet_f:
    reader = csv.reader(subnet_f)
    next(reader)    # Skip column names

    # Create list of subnets sorted by network address and
    # list of network addresses in the same order
    subnets = sorted((ipaddress.IPv4Network(sanitize(row[0])) for row in reader),
                     key=lambda x: x.network_address)
    network_addrs = [subnet.network_address for subnet in subnets]

with open('ip.csv') as ip_f, open('output.csv', 'w', newline='') as out_f:
    reader = csv.reader(ip_f)
    next(reader)

    writer = csv.writer(out_f)
    writer.writerow(['Subnet', 'IP'])

    for row in reader:
        ip = ipaddress.IPv4Address(sanitize(row[0]))
        index = bisect.bisect(network_addrs, ip) - 1

        if index < 0 or subnets[index].broadcast_address < ip:
            continue    # IP not in range of any networks
        writer.writerow([subnets[index], ip])

输出:

Subnet,IP
10.20.3.0/24,10.20.3.56

上面的时间复杂度为 O(n log m),其中 n 是 IP 的数量和 m 的网络数量。请注意,它仅与 Python 3 一起运行,因为ipaddress 不包含在 Python 2.7 中。如果您需要使用 Python 2.7,可以使用 backports

更新 高效解决方案的第一个目标是找到一种方法以高效的方式处理每个单独的 IP。遍历所有子网非常昂贵,因此不会这样做。最好为每个子网中的第一个 IP 创建一个排序列表。对于给定的数据,它看起来像这样:

[IPv4Address('10.20.3.0'), IPv4Address('10.176.122.136')]

这将允许我们执行二进制搜索以找到等于或低于单个 IP 的 IP 地址的索引。例如,当我们搜索 IP 10.20.3.56 时,我们使用bisect.bisect 为我们提供第一个大于 IP 的索引并将其减一:

>>> l = [IPv4Address('10.20.3.0'), IPv4Address('10.176.122.136')]
>>> index = bisect.bisect(l, IPv4Address('10.20.3.56'))
>>> index
1
>>> l[index - 1]
IPv4Address('10.20.3.0')

由于我们已经将网络存储到另一个列表中,该列表的顺序相同,我们可以使用索引来检索给定的子网。一旦我们有了子网,我们仍然需要检查单个 IP 是否等于或低于子网中的最后一个 IP。如果单个 IP 在子网内,则写入一行结果,如果不移动到下一个 IP。

【讨论】:

  • Python3 并且可以为大部分数据运行脚本。我遇到的问题是,如果有 IPv6 地址,它就会失败。如果有人有前导零,我也会遇到问题。例如,如果有一个 IP 像 192.168.010.199 而不是 192.168.10.199。另外,如果我的源文件有两列以上怎么办?
  • @DDIGuy 更新了处理更长行的答案,进行输入清理以删除前导零并添加更长的解释。
  • 感谢您的解释。如果我混合使用 IPv4 和 IPv6,有什么建议吗?我应该为 IPv4 运行一次,然后再为 IPv6 运行一次吗?或者有什么简单的方法可以在这里添加。
  • @DDIGuy:可能最简单的方法是拥有两个脚本,一个用于 IPv4,另一个用于 IPv6。修改跳过 IPv6 和另一个使用 IPv6 相关类并跳过 IPv4 应该不难。
  • 感谢您的帮助。明天我会试一试并发布结果。
猜你喜欢
  • 1970-01-01
  • 2017-05-16
  • 1970-01-01
  • 2018-04-03
  • 2019-01-08
  • 2012-06-28
  • 2020-11-01
  • 2018-10-07
  • 2012-05-08
相关资源
最近更新 更多