3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonでIPアドレスをいい感じにソートするいくつかの方法

Last updated at Posted at 2022-10-13

はじめに

 IPアドレスやネットワークアドレスを並び替えるときに何も考えずにソートすると、思い通りに並ばないので色々と工夫しないといけないと思います。

 例えば192.168.1.18.8.8.8の2つのIPアドレスを並び替える場合、普通は文字列型のソートになるので、表1のように並んでしまいます。
 これを表2のようにPythonでいい感じに並べてみたい。

表1. 普通のソート
192.168.1.1
8.8.8.8       
表2. いい感じのソート
8.8.8.8       
192.168.1.1

2022/10/19 追記:
 色々書いてみましたが、結論から言うと、@shiracamusさんからの的確なコメントにあるようにkeyとしてipaddressライブラリのIPv4Addressクラスを利用する手段が簡単です。

python sort_ipaddr.py

#ライブラリを読み込む
#192.168.1.0/24のようなCIDR表記のネットワークアドレスをソートしたい場合はIPv4Addressではなく、ip_network

from ipaddress import IPv4Address, ip_network

#ソートするIPアドレスをリストに格納する
ipaddr_list = ['192.168.1.1', '8.8.8.8']

#IPv4Addressをkeyとして、ソート
ipaddr_list_sorted_ipaddress = [*map(str, sorted(ipaddr_list, key=IPv4Address))]


#ipaddr_list_sorted_ipaddress = [*map(str, sorted(ipaddr_list, key=ip_network))]

print(ipaddr_list_sorted_ipaddress)

 結果

['8.8.8.8', '192.168.1.1']

1. sortedのkeyとしてタプルを活用しソートする

 IPアドレスをPythonでいい感じにソートできないかなと思い、検索したところ以下のサイトがヒットした。
 この回答を参考にして、いい感じにソートしてみる。

・準備

 必要なライブラリの読み込み。

#正規表現を扱うライブラリ
import re

 データの準備。

#ソートするIPアドレスをリストに格納する(192.168.1.0/24のようなCIDR表記でも可)
ipaddr_list = ['192.168.1.1', '8.8.8.8']

・関数定義

 sortedで使用するkeyの関数my_keyを定義する。
 keyは以下のようにIPアドレスをオクテットで区切り、タプルを作って、このソート順を利用する。

テキスト タプル
ソート順 ソート順
'192.168.1.1' (192, 168, 1, 1)
'8.8.8.8' (8, 8, 8, 8)
#文字型のIPアドレスを.と/で分割し、4(ネットワークアドレスの場合は5)つの数値を持つタプルに格納する
def split(ip):
    octet_prefix = tuple(int(part) for part in re.split(r'[./]', ip))
    return octet_prefix

#keyとしてタプルの順を定義
def my_key(item):
    return split(item)

・keyとして関数my_keyを指定して実行

ipaddr_list_sorted = sorted(ipaddr_list, key=my_key)
print(ipaddr_list_sorted)

 いい感じ:heart:じゃないですか?

['8.8.8.8', '192.168.1.1']

・試しにkeyを指定しないで実行

ipaddr_list_sorted_normal = sorted(ipaddr_list)
print(ipaddr_list_sorted_normal)

 ですよね:expressionless:

['192.168.1.1', '8.8.8.8']

 実戦投入する際はデータに数字, .及び/以外の文字列があるとint関数でValueErrorが発生するので、例えば以下のようにIPアドレス、もしくはCIDR表記のネットワークアドレス以外を除外してあげる必要があります。

#IPアドレス, ネットワークアドレスにマッチする正規表現
ptn = r'^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([1-2]?[0-9]|[3][0-2])|)$'

#正規表現にマッチする要素だけを新しいリストに格納する
ipaddr_list_filtered = list(filter(lambda x: re.search(ptn, x), ipaddr_list))

 なお、この一見したところ呪文のようなIPアドレス, ネットワークアドレスにマッチする正規表現の意味についてはこちらのこちらの記事を参照してください。

2. PythonでIPアドレスを扱うときに超絶便利なライブラリnetaddrを使ってソートする

 もう一つの方法、関数の定義などをしなくても、PythonでIPアドレスを扱うときに超絶便利なライブラリnetaddrを使えばもう少し簡単にソートできる。
 なお、この超便利なライブラリについてはこちらの記事で触れています。

・準備

 必要なライブラリのインストールと読み込み。

pip install netaddr
#便利なライブラリのnetaddrを読み込む
from netaddr import IPAddress, IPNetwork

 データの準備。

#ソートするIPアドレスをリストに格納する
ipaddr_list = ['192.168.1.1', '8.8.8.8']

・実行する

#netaddrのIPNetworkオブジェクトはそのままでいい感じにソートできるので、keyを使わずにソートして、その後、IPNetworkオブジェクトを文字列に変換する。
#192.168.1.0/24のようなCIDR表記のネットワークアドレスをソートしたい場合はIPAddressではなく、IPNetworkを利用する

#@shiracamusさんに頂いたコメントに沿って修正
#ipaddr_list_sorted_netaddr = list(map(str, sorted(list(map(IPAddress, ipaddr_list)))))

ipaddr_list_sorted_netaddr = list(map(str, sorted(map(IPAddress, ipaddr_list))))

print(ipaddr_list_sorted_netaddr)

 やっぱり、いい感じ:heart:

['8.8.8.8', '192.168.1.1']

さいごに

 このようにIPアドレスやネットワークアドレスをいい感じにソートしてみましたが、これを利用したサイトを立ててますので、よかったらご利用ください。

誰も使ってくれなかったこのサイトは残念ながら閉鎖しました...

以上

3
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?