'How to sort a list of lists with IP subnets python
I am a complete noob and have google made my first python script.
I am opening a 2 files and removing list 1 from list2.
Once list2 has been modified to remove what was in list 1, I want to sort the list by IP network. for example:
1.1.1.1/24
1.1.1.1/32
5.5.5.5/20
10.10.11.12/26
10.11.10.4/32
currently it is sorting
1.1.1.1/24
1.1.1.1/32
10.10.11.12/26
10.11.10.4/32
5.5.5.5/20
code:
import os
import sys
import random
import re
text_file = open("D:/file/update2.txt", "rt")
lines = str(text_file.readlines())
text_file.close()
ip_address = r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]
{1,3}/\d{1,2})'
foundip = re.findall( ip_address, lines )
text_file2 = open("D:/file/Block.txt", "rt")
lines2 = str(text_file2.readlines())
text_file2.close()
foundip2 = re.findall( ip_address, lines2 )
test =(list(set(foundip2) - set(foundip)))
items = sorted(test)
print (*items, sep = "\n")
Thanks in advance.
Solution 1:[1]
the default sort is alphanumeric sort. You need to generate integer tuples from your ip addresses to use as your sort key function. I use re.findall
with "digits" expression then convert to int (but there are other solutions, with split
for instance)
import re
ip_list = """1.1.1.1/24
1.1.1.1/32
10.10.11.12/26
10.11.10.4/32
5.5.5.5/20""".splitlines()
print(sorted(ip_list,key=lambda x : [int(m) for m in re.findall("\d+",x)]))
prints:
['1.1.1.1/24', '1.1.1.1/32', '5.5.5.5/20', '10.10.11.12/26', '10.11.10.4/32']
Solution 2:[2]
Your problem stems from the fact that alphabetic sorting is not doing what you want it to do ( finding 10.x to be 'smaller' than 5.x ). Therefore you need to pass a function that will transform the ip address string into numbers, so that this function follows your intuition of what should come first.
Solution : I will first create a type for IP where I will parse the string into 4 groups and the port, and compare based on these tuples (see python tuple sorting).
from collections import namedtuple
ip_type = namedtuple("IP", 'g1 g2 g3 g4 port')
def to_ip(string: str) -> ip_type:
groups, port = string.split('/')
g1, g2, g3, g4 = [int(g) for g in groups.split('.')]
return ip_type(g1, g2, g3, g4, int(port) )
array = [ '1.1.1.1/24',
'1.1.1.1/32',
'10.10.11.12/26',
'10.11.10.4/32',
'5.5.5.5/20' ]
print(sorted(array, key=to_ip))
Solution 3:[3]
You can avoid using complex regex or other solutions by using the ipaddress module from the python standard lib. This converts your strings into IP_network objects which are sortable.
from ipaddress import ip_network
ip_list = ["3.4.5.0/24", "1.2.3.0/24", "10.10.10.0/24", "5.6.7.0/24"]
print(sorted(ip_list, key=lambda x: ip_network(x)))
prints:
['1.2.3.0/24', '3.4.5.0/24', '5.6.7.0/24', '10.10.10.0/24']
You can also use the ipaddress library to detect if a string is a IP address and replace your regex. Regex is pretty slow in python, so you should avoid it if you can and need the speed.
ip_list = []
for ip in string_list:
try:
ip_list.append(ipaddress.ip_network(ip))
except ValueError:
pass
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Jean-François Fabre |
Solution 2 | ikamen |
Solution 3 |