我有一段看起来像这样的代码:
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,但我不确定如何在我的代码中实现它,或者甚至可能因为我使用字典。我有什么选择?谢谢..
答案 0 :(得分:32)
您可以使用自定义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]))
答案 1 :(得分:12)
使用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提出了一个使用套接字模块的解决方案,所以为什么不使用它来进行从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])
答案 2 :(得分:3)
如果您的应用程序执行了很多操作,例如“在范围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'
答案 3 :(得分:2)
我有什么选择?
我想到的两个显而易见的是:
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似乎已经为这种方法提供了一个很好的解决方案! :)
答案 4 :(得分:2)
在https://www.lesinskis.com/python_sorting_IP_addresses.html找到了解决方案 您所要做的就是在ipaddress中转换ip的字符串
import ipaddress
sortedkey = sorted(list_of_ip_instring, key = ipaddress.IPv4Address)
答案 5 :(得分:1)
我认为这会对您有所帮助:PEP265(按值排序字典)。只需扩展已排序的函数。
答案 6 :(得分:1)
一种处理正确订单的干净方法是使用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)
答案 7 :(得分:0)
完全不使用字符串而是将每个八位位组转换为整数,然后将其传递到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
比较也可能是有益的。