Как отсортировать IP-адреса, хранящиеся в словаре в Python? - PullRequest
15 голосов
/ 01 июля 2011

У меня есть кусок кода, который выглядит следующим образом:

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

Я нашел это , но я не уверен, какреализовать это в моем коде, или, если это возможно, так как я использую словарь.Какие у меня есть варианты?Спасибо ..

Ответы [ 7 ]

32 голосов
/ 01 июля 2011

Вы можете использовать пользовательскую функцию 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 есть встроенная поддержка лексикографической сортировки кортежей.

ОБНОВЛЕНИЕ : На самом деле это можно сделать еще проще, используя функцию inet_aton() в модуле socket:

import socket
items = sorted(ipCount.items(), key=lambda item: socket.inet_aton(item[0]))
12 голосов
/ 01 июля 2011

Используйте ключевой параметр sorted для преобразования вашего 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])
3 голосов
/ 01 июля 2011

, если ваше приложение делает много вещей, таких как «найти ips в диапазоне x», «сортировать по 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'
2 голосов
/ 01 июля 2011

Какие у меня есть варианты?

Два очевидных, которые приходят мне в голову:

  1. Предварительное форматирование строк с IP при сохранении их по ссылке, указанной в вашем вопросе.
  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

Пример функции сортировки

Ну ... Фердинанд Бейер в его ответ , кажется, уже предложил отличное решение для этого подхода! :)

1 голос
/ 19 мая 2019

Чистым способом обработки правильного порядка является использование модуля 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)
1 голос
/ 01 июля 2011

Думаю, это вам поможет: PEP265 (сортировка словарей по значению). Просто расширите отсортированную функцию.

0 голосов
/ 08 февраля 2019

как насчет того, чтобы вообще не работать со строками и вместо этого конвертировать каждый октет в целое число, а затем передавать его в четырехмерный словарь??

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.

...