如何在 Python 中对存储在字典中的 IP 地址进行排序



我有一段代码看起来像这样:

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)

我在这里有什么选择?

我想到的两个明显的是:

  1. 当您将字符串存储为来自您放入问题的链接时,请使用 IP 预先格式化字符串
  2. 执行排序时,将
  3. 排序函数传递给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进行比较可能是有益的。

最新更新