简体   繁体   English

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

[英]How to sort IP addresses stored in dictionary in Python?

I have a piece of code that looks like this:我有一段代码看起来像这样:

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)

and it kind of sorts IP's, but like this:它对 IP 进行排序,但像这样:

192.168.102.105
192.168.204.111
192.168.99.11

which is not good enough since it does not recognize that 99 is a smaller number than 102 or 204. I would like the output to be like this:这还不够好,因为它无法识别 99 是小于 102 或 204 的数字。我希望 output 是这样的:

192.168.99.11
192.168.102.105
192.168.204.111

I found this , but I am not sure how to implement it in my code, or if it is even possible since I use dictionary.我找到了这个,但我不确定如何在我的代码中实现它,或者因为我使用字典,它是否有可能实现。 What are my options here?我在这里有什么选择?

You can use a custom key function to return a sortable representation of your strings:您可以使用自定义key function 来返回字符串的可排序表示:

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)

The split_ip() function takes an IP address string like '192.168.102.105' and turns it into a tuple of integers (192, 168, 102, 105) . split_ip() function 采用 IP 地址字符串,如'192.168.102.105'并将其转换为整数元组(192, 168, 102, 105) Python has built-in support to sort tuples lexicographically. Python 内置支持按字典顺序对元组进行排序。

UPDATE : This can actually be done even easier using the inet_aton() function in the socket module:更新:这实际上可以使用socket模块中的inet_aton() function 更容易地完成:

import socket
items = sorted(ipCount.items(), key=lambda item: socket.inet_aton(item[0]))

Use the key parameter of sorted to convert your ip to an integer, for example:使用 sorted 的关键参数将您的 ip 转换为 integer,例如:

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))

EDIT:编辑:

Gryphius proposes a solution with the socket module, and so why not use it to make the conversion from ip to long as it is cleaner: 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])

A clean way of handling the right order is using Pythons ipaddress module.一种处理正确顺序的干净方法是使用 Python 的ipaddress模块。 You can transform the Strings into IPv4Address representations and sort them afterwards.您可以将字符串转换为IPv4Address表示形式,然后对其进行排序。 Here's a working example with list objects (Tested with Python3):这是一个带有列表对象的工作示例(使用 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)

Found a solution at https://www.lesinskis.com/python_sorting_IP_addresses.html All you have to do is convert the string of ip in ipaddresshttps://www.lesinskis.com/python_sorting_IP_addresses.html找到了解决方案您所要做的就是将 Z957B527BFBAD2E80F58D206839314 中的字符串转换为 Z957B527BCFBAD2E80F58D206839314

import ipaddress
sortedkey = sorted(list_of_ip_instring, key = ipaddress.IPv4Address)

if your application does lots of things like "find ips in range x", "sort by ip" etc its often more convenient to store the numeric value of the ip internally and work with this one.如果您的应用程序做了很多事情,例如“在 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

convert the number back into an ip using this function:使用此 function 将数字转换回 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'

What are my options here?我在这里有什么选择?

The two obvious one that come to my mind are:我想到的两个明显的是:

  1. Preformatting the strings with the IP when you store them as from the link you put in your question.使用 IP 对字符串进行预格式化,当您将它们存储为您在问题中输入的链接时。
  2. Pass a sorting function to sorted() function when you perform the ordering.执行排序时,将排序 function 传递sorted() function。

Which is best depends from the amount of data you have to process (you will notice an increased performance for method #1 only for very large amount of data) and from what you will need to do with said sorted list of IP (if you preformat the strings, you might then need to change them again before feeding them as arguments to other functions, for example).哪个最好取决于您必须处理的数据量(您会注意到方法#1 的性能提高仅适用于大量数据)以及您需要对 IP 的排序列表执行的操作(如果您预先格式化例如,您可能需要再次更改它们,然后再将它们作为 arguments 提供给其他函数)。

Example of preformatting预格式化示例

Maintain the IP as a string, but uses spaces or zeroes to solve the variable number of digits problem:将 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

Example of sorting function排序示例 function

Well... Ferdinand Beyer in his answer seems to have already offered an excellent solution for this approach: :)好吧... Ferdinand Beyer 在他的回答中似乎已经为这种方法提供了一个很好的解决方案::)

in python 3在 python 3

use like this:像这样使用:

import ipaddress

clientIp = sorted(clientIp, key=ipaddress.IPv4Address)

for ip in clientIp:
    print(ip)

and when IP addresses are Classless Inter-Domain Routing (CIDR) use:当 IP 地址是无类域间路由 (CIDR) 时,请使用:

import ipaddress

clientIp = sorted(clientIp, key=ipaddress.IPv4Network)

for ip in clientIp:
    print(ip)

I think this will help you: PEP265 (sorting dictionieries by value).我认为这会对您有所帮助: PEP265 (按值排序字典)。 Just extend the sorted function.只需扩展排序后的 function。

how about not working with strings at all and instead convert each octet into integer, then passing it into 4 dimensional dictionary?根本不使用字符串,而是将每个八位字节转换为 integer,然后将其传递到 4 维字典中怎么样?

ClientIps[192][168][102][105]=1
ClientIps[192][168][99][11]=1

then it is easy to just sort an array by key, isnt it?那么很容易通过键对数组进行排序,不是吗?

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)

for speed reasons it may be beneficial to also compare simple python dictionary against OrderedDict .出于速度原因,将简单的 python 字典与OrderedDict进行比较可能是有益的。

If you want to sort a list of IPv4/6 address strings, the correct and simplest solution, for python3 at least, is to convert to ipaddress objects and sort with ipaddress.get_mixed_type_key .如果要对 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM