В сети любой специалист сталкивается с задачей упорядочить список IP‑адресов. На первый взгляд кажется, что достаточно применить обычную сортировку строк, но в реальности такой подход приводит к ошибкам: 10.11.2.1 окажется после 10.11;100.1 и т.д. В статье разберём, почему так происходит, как работают маски подсетей (/24, /16 и др.), а также представим готовые решения для самых популярных сред: Python, Bash, MySQL, Excel, PowerShell.
Почему обычная строковая сортировка не подходит?
IP‑адрес в традиционной записи состоит из четырёх октетов, разделённых точками:
AAA.BBB.CCC.DDD
При лексикографической (строковой) сортировке сравниваются символы слева направо. Поэтому строка 10.11.100.1 считается «меньше», чем 10.11.2.1, потому что символ 1 (из 100) меньше 2. Чтобы получить корректный порядок, каждый октет необходимо сравнивать как целое число.
Маски подсетей и их роль в сортировке
Маска подсети определяет, какие биты IP‑адреса относятся к сети, а какие – к хосту. Записывается в виде /N, где N – количество «единичных» битов слева.
- /32 – 255.255.255.255 – один единственный адрес.
- /24 – 255.255.255.0 – 8 битов под хосты (256 адресов).
- /16 – 255.255.0.0 – 16 битов под хосты (65 536 адресов).
- … и т.д..
Пример: сеть 192.168.0.0/24 охватывает диапазон от 192.168.0.0 до 192.168.0.255. При сортировке внутри такой подсети порядок будет определяться только последним октетом, но если в списке присутствуют адреса из разных подсетей (/24, /16), то необходимо учитывать все четыре октета.
Алгоритм «правильной» сортировки
- Разбить каждый адрес на четыре части.
- Преобразовать каждую часть в целое число.
- Сравнивать адреса последовательно: первый октет → второй → третий → четвёртый.
- Опционально: преобразовать адрес в 32‑битное целое (
uint32) и сортировать уже его.
Последний пункт особенно удобен, когда нужно быстро сравнивать большие массивы (десятки‑сотни тысяч записей).
Практические решения
Bash / Linux (команда sort -t . -k1,1n -k2,2n -k3,3n -k4,4n)
# Пример файла ip.txt
10.11.10.11
10.11.10.222
10.11.12.13
10.11.102.3
sort -t . -k1,1n -k2,2n -k3,3n -k4,4n ip.txt
Опция -t . задаёт разделитель, а -kX,Xn указывает числовую сортировку по X‑му полю.
Python (модуль ipaddress)
import ipaddress
ips = [
"10.11.10.11",
"10.11.10.222",
"10.11.12.13",
"10.11.102.3"
]
sorted_ips = sorted(ips, key=ipaddress.IPv4Address)
print("
".join(sorted_ips))
Библиотека ipaddress входит в стандартный набор Python 3, поэтому дополнительных установок не требуется.
MySQL / MariaDB
Для сортировки IP‑адресов в запросе используют функцию INET_ATON, которая переводит строку в 32‑битное целое.
SELECT ip
FROM devices
ORDER BY INET_ATON(ip);
Если необходимо получить обратно строковое представление, используйте INET_NTOA.
Excel
Самый простой способ – добавить вспомогательный столбец, где IP‑адрес переводится в десятичный вид:
- Допустим, адрес в ячейке
A2. - В ячейку
B2введите формулу:
=SUMPRODUCT(TEXTSPLIT(A2,".")*{16777216;65536;256;1})(Excel 365)
Для более старых версий используйте комбинациюLEFT,MID,RIGHTиVALUE. - Скопируйте формулу вниз, затем сортируйте по столбцу
B.
После сортировки столбец B можно скрыть.
PowerShell
# Список IP‑адресов
$ips = @(
'10.11.10.11',
'10.11.10.222',
'10.11.12.13',
'10.11.102.3'
)
$sorted = $ips | ForEach-Object { [System.Net.IPAddress]::Parse($_) } |
Sort-Object
$sorted | ForEach-Object { $_.ToString }
Тип IPAddress реализует сравнение по числовому значению, поэтому сортировка «из коробки» работает корректно.
Сортировка больших наборов (пример ≈ 2416 записей)
В реальных проектах часто требуется обработать несколько тысяч IP‑адресов. Ниже – рекомендации, как обеспечить эффективность.
- Преобразование в 32‑битные числа – экономит память и ускоряет сравнение. В Python это
int(ipaddress.IPv4Address(addr)), в Bash –printf "%uпосле
"awk. - Использование внешних сортировщиков (например,
sort -uв Linux) позволяет работать с файлами, превышающими объём ОЗУ. - Индексация в базах данных – храните IP как
INT UNSIGNEDи создавайте индекс. Сортировка будет происходить за O(log N). - Параллельная обработка – в Python можно воспользоваться
multiprocessing.Poolдля конвертации, а затем объединить результаты.
Пример скрипта, который читает файл ips.txt (≈ 2416 строк), преобразует адреса в числа, сортирует и выводит обратно в привычный формат:
#!/usr/bin/env python3
import ipaddress, sys
def main(path):
with open(path) as f:
# Читаем, убираем пробелы и переводим в целые числа
nums = [int(ipaddress.IPv4Address(line.strip)) for line in f if line.strip]
# Сортируем
nums.sort
for n in nums:
print(str(ipaddress.IPv4Address(n)))
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: sort_ips.py ', file=sys.stderr)
sys.exit(1)
main(sys.argv[1])
Скрипт обрабатывает любой объём данных, ограниченный лишь доступной памятью (для 2416 записей это менее 1 МБ).
Часто задаваемые вопросы
Что делать, если в списке есть смешанные IPv4 и IPv6?
Разделите их на два списка и сортируйте отдельно. Для IPv6 используйте ipaddress.IPv6Address (Python) или INET6_ATON (MySQL).
Как сортировать диапазоны (CIDR), а не отдельные адреса?
Сначала «разворачиваем» диапазоны в отдельные адреса (если нужно), либо сортируем по начальному адресу сети и длине маски. В Python:
import ipaddress
net = ipaddress.ip_network('192.168.0.0/24')
print(int(net.network_address), net.prefixlen) # (3232235520, 24)
Почему sort -n иногда «ломает» порядок?
Опция -n сравнивает только первую найденную числовую последовательность. При IP‑адресах нужно явно указать, какие поля сравнивать (-k1,1n -k2,2n …).
Сортировка IP‑адресов – задача, требующая внимания к деталям:
- Разбивать адреса на октеты и сравнивать их как числа.
- Для больших наборов удобно переводить адрес в 32‑битное целое.
- В разных средах существуют готовые функции (
ipaddress,INET_ATON,System.Net.IPAddress), которые делают работу безопасной и быстрой.
Следуя рекомендациям из этой статьи, вы сможете безошибочно упорядочить любые списки – от десятков до десятков‑тысяч записей, включая «2416‑элементный» набор, о котором шла речь в заголовке.
Дата публикации: г. | Автор: виртуальный помощник ChatGPT