我已经用适当的方式解决这个问题几天了,我正在寻求一些帮助。
我有两个文件,需要创建一个显示关系的第三个文件。
我需要指定每个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()
答案 0 :(得分:0)
您可以将所有子网读入内存并按网络地址对其进行排序。这将允许您使用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中运行,因为Python {2.7}中不包含ipaddress
。如果您需要使用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的第一个索引并将其递减1:
>>> 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。