2011-07-01 831 views
10

Tengo un pedazo de código que se parece a esto:¿Cómo ordenar las direcciones IP almacenadas en el diccionario en Python?

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) 

y como que ordena IP, pero como esto:

192.168.102.105 
192.168.204.111 
192.168.99.11 

que no es lo suficientemente bueno, ya que no reconoce que el 99 es un número menor que 102 o 204. me gustaría que la salida sea así:

192.168.99.11 
192.168.102.105 
192.168.204.111 

encontré this, pero no estoy seguro de cómo impl hacerlo en mi código, o si es posible ya que uso el diccionario. ¿Cuáles son mis opciones aquí? Gracias ..

Respuesta

24

Usted puede utilizar una costumbre key función para devolver una representación sortable de sus cadenas:

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) 

La función split_ip() toma una cadena como la dirección IP '192.168.102.105' y lo convierte en una tupla de enteros (192, 168, 102, 105). Python tiene soporte integrado para ordenar tuplas lexicográficamente.

ACTUALIZACIÓN: En realidad, esto se puede hacer aún más fácil el uso de la función inet_aton() en el módulo socket:

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

También puede hacer esto con la función 'map':' ordenados (ipCount.items (clave), lambda = x: tuple (map (int, x.split ('.'))))) ' – Handyman5

+0

Veo que las respuestas actualizadas que usan inet_aton son diferentes: la llamada de Ludo struct.unpack y la de Ferdinand no. ¿Es necesaria esta función para ordenar o no? –

+0

@randomtoor: es * no * necesario. 'inet_aton' devuelve una cadena de 4 caracteres, y Python sabe cómo comparar y ordenar cadenas. –

2

¿Cuáles son mis opciones aquí?

Los dos obvia que vienen a la mente son:

  1. preformateado las cuerdas con el IP cuando se los almacena como desde el enlace que pones en tu pregunta.
  2. Pase una función de clasificación a sorted() función cuando realiza el pedido.

¿Qué es mejor depende de la cantidad de los datos tiene que procesar (se advertirá un mayor rendimiento para el método # 1 sólo para la gran cantidad de datos) y por lo que tendrá que ver con dicho lista clasificada de IP (si preformatea las cadenas, puede necesitar cambiarlas de nuevo antes de alimentarlas como argumentos para otras funciones, por ejemplo).

Ejemplo de preformatear

Mantener la IP como una cadena, pero utiliza espacios o ceros para resolver el número variable de problema dígitos:

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

Ejemplo de la función

clasificación

Bueno ... ¡Ferdinand Beyer en his answer parece haber ofrecido una excelente solución para este enfoque!:)

10

utilizar el parámetro clave de la ordenada para convertir su IP a un número entero, por ejemplo:

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 propone una solución con el módulo de toma de corriente, y por qué no usarlo para hacer la conversión de IP al tiempo que es más limpio:

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

creo que esto le ayudará a: PEP265 (clasificación dictionieries términos de valor). Simplemente extienda la función ordenada.

2

si su aplicación hace muchas cosas como "encontrar ips en el rango x", "ordenar por ip" etc., a menudo es más conveniente almacenar el valor numérico de la IP internamente y trabajar con esta.

from socket import inet_aton,inet_ntoa 
import struct 

def ip2long(ip): 
    packed = inet_aton(ip) 
    lng = struct.unpack("!L", packed)[0] 
    return lng 

convertir el número de nuevo en una ip usando esta función:

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' 
+0

+1 para recomendar el módulo 'socket'. Pero dado que la pregunta es acerca de la clasificación, es posible que desee dar un ejemplo sobre cómo usarlo en este contexto. –

+0

sixfeetsix acaba de hacer :-) – Gryphius

+0

Sí, yo también;) –

Cuestiones relacionadas