Простой Python. Современный стиль программирования (Любанович) - страница 113

Используем логотип издательства 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(). Спецификаторы порядка байтов располагаются первыми в строке формата.