如何在 Python 中对存储在字典中的 IP 地址进行排序?
Posted
技术标签:
【中文标题】如何在 Python 中对存储在字典中的 IP 地址进行排序?【英文标题】:How to sort IP addresses stored in dictionary in Python? 【发布时间】:2011-09-26 13:33:45 【问题描述】:我有一段代码如下所示:
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
我找到了this,但我不确定如何在我的代码中实现它,或者因为我使用字典是否有可能。我在这里有什么选择?谢谢。。
【问题讨论】:
【参考方案1】:您可以使用自定义 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()
函数接受一个 IP 地址字符串,如'192.168.102.105'
,并将其转换为整数元组(192, 168, 102, 105)
。 Python 内置支持按字典顺序对元组进行排序。
更新:这实际上可以使用socket
模块中的inet_aton()
函数更轻松地完成:
import socket
items = sorted(ipCount.items(), key=lambda item: socket.inet_aton(item[0]))
【讨论】:
您也可以使用map
函数:sorted(ipCount.items(), key=lambda x:tuple(map(int, x.split('.')))))
我看到使用 inet_aton 的更新答案不同:Ludo 调用 struct.unpack 而 Ferdinand 没有。这个函数调用是否需要订购?
@randomtoor:没有必要。 inet_aton
返回一个 4 个字符的字符串,Python 知道如何对字符串进行比较和排序。
更新版太棒了!应该是主要版本并回答 IMO。【参考方案2】:
使用 sorted 的 key 参数将你的 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 提出了一个带有 socket 模块的解决方案,所以为什么不使用它来进行从 ip 到 long 的转换,因为它更干净:
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])
【讨论】:
我想给这个+1,但它需要一点解释。具体来说,为什么[0]
?【参考方案3】:
处理正确订单的一种简洁方法是使用 Pythons 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)
【讨论】:
【参考方案4】:在https://www.lesinskis.com/python_sorting_IP_addresses.html 找到了解决方案 你所要做的就是转换ipaddress中的ip字符串
import ipaddress
sortedkey = sorted(list_of_ip_instring, key = ipaddress.IPv4Address)
【讨论】:
【参考方案5】:如果您的应用程序做了很多事情,例如“在 x 范围内查找 ips”、“按 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'
【讨论】:
+1 用于推荐socket
模块。但由于问题是关于排序的,你可能想举一个例子来说明如何在这种情况下使用它。【参考方案6】:
我有什么选择?
我想到的两个明显的是:
-
使用 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
排序函数示例
嗯...his answer 中的 Ferdinand Beyer 似乎已经为这种方法提供了一个很好的解决方案! :)
【讨论】:
【参考方案7】:我认为这会对您有所帮助:PEP265(按值对字典进行排序)。只需扩展 sorted 函数即可。
【讨论】:
【参考方案8】:完全不使用字符串,而是将每个八位字节转换为整数,然后将其传递到 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
进行比较可能会有所帮助。
【讨论】:
以上是关于如何在 Python 中对存储在字典中的 IP 地址进行排序?的主要内容,如果未能解决你的问题,请参考以下文章