Используем логотип издательства O’Reilly — изображение маленького долгопята с глазками-бусинами (рис. 7.1).
Рис. 7.1. Долгопят от издательства O’Reilly
Файл этого изображения с расширением PNG доступен в «Википедии». Мы не будем рассматривать чтение файла вплоть до главы 8, поэтому я загрузил этот файл, написал небольшую программу, которая выводит его значения как байты, и просто напечатал значения первых 30 байт как значения переменной типа bytes по имени data для примера, который следует далее. (Спецификация формата PNG предполагает, что ширина и высота хранятся в первых 24 байтах, поэтому нам пока что больше данных и не нужно.)
>>>> import struct
>>>> valid_png_header = b'\x89PNG\r\n\x1a\n'
>>>> data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR' + \
>…·····b'\x00\x00\x00\x9a\x00\x00\x00\x8d\x08\x02\x00\x00\x00\xc0'
>>>> if data[:8] == valid_png_header:
>…·····width, height = struct.unpack('>LL', data[16:24])
>…·····print('Valid PNG, width', width, 'height', height)
>… else:
>…·····print('Not a valid PNG')
>…
>Valid PNG, width 154 height 141
Этот код делает следующее.
• Переменная data содержит первые 30 байт файла PNG. Для того чтобы разместить ее на странице, я объединил две байтовые строки с помощью операторов + и \.
• Переменная valid_png_header содержит восьмибайтовую последовательность, которая обозначает начало корректного PNG-файла.
• Значение переменной width извлекается из 16–20-го байтов, а переменной height — из байтов 21–24.
>LL — это строка формата, которая указывает функции unpack(), как интерпретировать входные последовательности байтов и преобразовать их в типы данных Python. Рассмотрим ее детальнее:
• символ < означает, что целые числа хранятся в формате big-endian (обратный порядок байтов);
• каждый символ L определяет четырехбайтное целое число типа unsigned long.
Вы можете проверить значение каждого четырехбайтного набора непосредственно:
>>>> data[16:20]
>b'\x00\x00\x00\x9a'
>>>> data[20:24]0x9a
>b'\x00\x00\x00\x8d'
У целых чисел с обратным порядком байтов главный байт располагается слева. Поскольку значения ширины и длины меньше 255, они умещаются в последний байт каждой последовательности. Вы можете убедиться в том, что эти шестнадцатеричные значения соответствуют ожидаемым десятичным значениям:
>>>> 0x9a
>154
>>>> 0x8d
>141
Если вы хотите отправить их в противоположном направлении и преобразовать данные Python в байты, используйте функцию pack() модуля struct:
>>>> import struct
>>>> struct.pack('>L', 154)
>b'\x00\x00\x00\x9a'
>>>> struct.pack('>L', 141)
>b'\x00\x00\x00\x8d'
В табл. 7.5 и 7.6 показаны спецификаторы формата для функций pack() и unpack(). Спецификаторы порядка байтов располагаются первыми в строке формата.