我有一段代码看起来像这样:
ipCount = defaultdict(int)
for logLine in logLines:
date, serverIp, clientIp = logLine.split(" ")
ipCount[clientIp] += 1
for clientIp, hitCount in sorted(ipCount.items(), key=operator.itemgetter(0)):
print(clientIp)
它有点排序 IP,但像这样:
192.168.102.105
192.168.204.111
192.168.99.11
这还不够好,因为它不承认 99 比 102 或 204 小。我希望输出是这样的:
192.168.99.11
192.168.102.105
192.168.204.111
我发现了这个,但我不确定如何在我的代码中实现它,或者因为我使用字典,所以它是否可能。我在这里有什么选择?
自定义key
函数返回字符串的可排序表示形式:
def split_ip(ip):
"""Split a IP address given as string into a 4-tuple of integers."""
return tuple(int(part) for part in ip.split('.'))
def my_key(item):
return split_ip(item[0])
items = sorted(ipCount.items(), key=my_key)
split_ip()
函数采用像'192.168.102.105'
这样的 IP 地址字符串,并将其转换为整数元组(192, 168, 102, 105)
。 Python 内置支持按字典顺序对元组进行排序。
更新:这实际上可以使用socket
模块中的inet_aton()
函数更轻松地完成:
import socket
items = sorted(ipCount.items(), key=lambda item: socket.inet_aton(item[0]))
使用 sort 的键参数将 ip 转换为整数,例如:
list_of_ips = ['192.168.204.111', '192.168.99.11', '192.168.102.105']
sorted(list_of_ips, key=lambda ip: long(''.join(["%02X" % long(i) for i in ip.split('.')]), 16))
编辑:
Gryphius 提出了一个带有套接字模块的解决方案,那么为什么不使用它来进行从 ip 到只要它更干净的转换:
from socket import inet_aton
import struct
list_of_ips = ['192.168.204.111', '192.168.99.11', '192.168.102.105']
sorted(list_of_ips, key=lambda ip: struct.unpack("!L", inet_aton(ip))[0])
处理正确顺序的一种干净方法是使用 Python 的 ipaddress 模块。您可以将字符串转换为 IPv4Address 表示形式,然后对其进行排序。下面是列表对象的工作示例(使用 Python3 测试(:
import ipaddress
unsorted_list = [
'192.168.102.105',
'192.168.204.111',
'192.168.99.11'
]
new_list = []
for element in unsorted_list:
new_list.append(ipaddress.ip_address(element))
new_list.sort()
# [IPv4Address('192.168.99.11'), IPv4Address('192.168.102.105'), IPv4Address('192.168.204.111')]
print(new_list)
在 https://www.lesinskis.com/python_sorting_IP_addresses.html 找到了一个解决方案您所要做的就是转换 ipaddress 中的 ip 字符串
import ipaddress
sortedkey = sorted(list_of_ip_instring, key = ipaddress.IPv4Address)
如果您的应用程序执行很多操作,例如"在范围x中查找IP","按IP排序"等,那么在内部存储IP的数值并使用此值通常更方便。
from socket import inet_aton,inet_ntoa
import struct
def ip2long(ip):
packed = inet_aton(ip)
lng = struct.unpack("!L", packed)[0]
return lng
使用此功能将数字转换回 IP:
def long2ip(lng):
packed = struct.pack("!L", lng)
ip=inet_ntoa(packed)
return ip
>>> ip2long('192.168.1.1')
3232235777
>>> ip2long('1.2.3.4')
16909060
>>> long2ip(3232235777)
'192.168.1.1'
>>> long2ip(16909060)
'1.2.3.4'
in Python 3
像这样使用:
import ipaddress
clientIp = sorted(clientIp, key=ipaddress.IPv4Address)
for ip in clientIp:
print(ip)
当 IP 地址为无类别域间路由 (CIDR( 时,请使用:
import ipaddress
clientIp = sorted(clientIp, key=ipaddress.IPv4Network)
for ip in clientIp:
print(ip)
我在这里有什么选择?
我想到的两个明显的是:
- 当您将字符串存储为来自您放入问题的链接时,请使用 IP 预先格式化字符串。 执行排序时,将
- 排序函数传递给
sorted()
函数。
哪个最好取决于您必须处理的数据量(您会注意到方法 #1 的性能有所提高,仅适用于非常大量的数据(以及您需要对所述排序的 IP 列表执行的操作(如果您预先格式化字符串,您可能需要在将它们作为参数提供给其他函数之前再次更改它们, 例如(。
预格式化示例
将 IP 维护为字符串,但使用空格或零来解决可变位数问题:
>>> ip = '192.168.1.1'
>>> print('%3s.%3s.%3s.%3s' % tuple(ip.split('.')))
192.168. 1. 1
>>> print('%s.%s.%s.%s' % tuple([s.zfill(3) for s in ip.split('.')]))
192.168.001.001
排序功能示例
井。。。费迪南德·拜尔(Ferdinand Beyer(在他的回答中似乎已经为这种方法提供了一个很好的解决方案!:)
我认为这会对您有所帮助:PEP265(按值对字典进行排序(。只需扩展排序功能。
如果要对IPv4/6地址字符串列表进行排序,至少对于python3,正确和最简单的解决方案是转换为ipaddress对象并使用ipaddress.get_mixed_type_key进行排序。
import ipaddress
unsorted_ips = [
"1.1.1.1",
"1.0.0.1",
"2606:4700:4700::1111",
"2606:4700:4700::1001",
]
sorted_ips = sorted(
[ipaddress.ip_address(ip) for ip in unsorted_ips], key=ipaddress.get_mixed_type_key
)
print(sorted_ips)
根本不使用字符串,而是将每个八位字节转换为整数,然后将其传递到 4 维字典中怎么样?
ClientIps[192][168][102][105]=1
ClientIps[192][168][99][11]=1
那么按键对数组进行排序很容易,不是吗?
for key1, value in sorted(ClientIps.items()):
for key2, value in sorted(ClientIps[key1].items()):
for key3, value in sorted(ClientIps[key1][key2].items()):
for key4, value in sorted(ClientIps[key][key2][key3].items()):
print(key1, key2, key3, key4)
出于速度原因,将简单的Python字典与OrderedDict
进行比较可能是有益的。