Двоичная информация в Python: типы и практические примеры
Работа с двоичными данными в Python
В Python двоичные данные представляются встроенными типами bytes и bytearray, а также модулями для их преобразования и анализа. Ниже приведены различные подходы к созданию, изменению и интерпретации бинарных данных.
Как эффективно упаковывать и распаковывать двоичные данные фиксированной структуры?
Для работы со структурированными двоичными данными (например, заголовки файлов, сетевые пакеты) применяется модуль struct. Он преобразует значения Python в байты и обратно в соответствии с форматом.
import struct
# Упаковка целого числа (4 байта) и строки (5 байт)
data = struct.pack('>I5s', 1024, b'hello')
print(data) # b'\x00\x00\x04\x00hello'
# Распаковка
a, b = struct.unpack('>I5s', data)
print(a, b) # 1024 b'hello'Set str python (множество из строки в python)
Проблема: несоответствие порядка байтов (endianness) или размера типов. Решение - явно указывать формат ('<' Little-Endian, '>' Big-Endian, '!' сетевой порядок).
Типичная ошибка: передача строки без кодировки - строки должны быть байтами (b'...').
Как создать и изменить неизменяемую последовательность байтов?
Тип bytes представляет неизменяемую последовательность байтов. Создать его можно из строки, целых чисел или итератора.
b1 = b'Hello'
b2 = bytes([72, 101, 108, 108, 111]) # то же самое
b3 = bytes(10) # 10 нулевых байтов
print(b1 == b2) # TruePython переменная время (переменные для времени в python)
Проблема: попытка изменить элемент bytes вызовет TypeError. Для изменений используйте bytearray.
Как изменить двоичные данные на месте?
bytearray - изменяемый аналог bytes. Позволяет присваивать значения по индексу или срезу.
ba = bytearray(b'Python')
ba[0] = 74 # 74 -> 'J'
ba[1:4] = b'AVA'
print(ba) # bytearray(b'JAVAn')
Python типы данных время (типы данных для времени в python)
Проблема: присваивание значения вне диапазона 0-255 вызывает ValueError. Необходимо проверять диапазон.
Как преобразовывать двоичные данные в шестнадцатеричный вид и обратно?
Модуль binascii предоставляет функции hexlify и unhexlify для работы с hex-строками.
import binascii
binary_data = b'\x00\x01\x02\xff'
hex_str = binascii.hexlify(binary_data).decode('ascii')
print(hex_str) # '000102ff'
original = binascii.unhexlify(hex_str)
print(original) # b'\x00\x01\x02\xff'Python объект тип (тип объекта в python)
Проблема: результат hexlify - объект bytes, его нужно декодировать для строкового представления. При unhexlify строка должна содержать только hex-символы, иначе binascii.Error.
Как эффективно обращаться к срезам без копирования данных?
memoryview позволяет получать представление буфера без копирования, что полезно для больших двоичных массивов.
data = bytearray(b'This is a buffer')
view = memoryview(data)
slice_view = view[5:10]
print(slice_view.tobytes()) # b'is a '
# Изменение через view влияет на оригинал
view[0] = 84 # 'T' -> ascii 84 (совпадает, просто пример)
print(data) # bytearray(b'This is a buffer')вещественные значения python (вещественные значения в python)
Проблема: memoryview не поддерживает операции, требующие изменения размера. Также при работе с многомерными массивами (например, из модуля array) нужно указывать формат.
Как читать и записывать двоичные файлы?
Для работы с бинарными файлами файл открывается в режиме 'rb' или 'wb'. Затем используются методы read, write или struct для упаковки.
with open('data.bin', 'wb') as f:
f.write(struct.pack('<Q', 123456789)) # 8 байт
with open('data.bin', 'rb') as f:
value = struct.unpack('<Q', f.read(8))[0]
print(value) # 123456789вывести тип данных python (вывод типа данных в python)
Проблема: несоответствие количества прочитанных байт ожидаемому - может привести к struct.error. Необходимо проверять длину данных.
Как выполнять битовые операции над байтами?
Битовые сдвиги, И, ИЛИ, НЕ применяются к целым числам. Для работы с отдельными битами байта можно преобразовать байт в int.
b = b'\x0f' # 15
val = b[0]
val |= 0x10 # устанавливаем 5-й бит
val &= 0x1F # маска для 5 бит
new_byte = bytes([val])
print(new_byte) # b'\x1f' (31)Проблема: при работе с большими последовательностями удобнее использовать массив целых чисел. Для представления битовой маски подходят целые числа произвольной длины (int).
Расширенные примеры работы с двоичными данными
Ниже представлены дополнительные сценарии с подробными пояснениями и выводом результатов.
Чтение бинарного файла заголовка BMP
import struct
# Пример содержимого BMP-файла (заголовок 54 байта)
# Для демонстрации создадим минимальный BMP (1x1 пиксель)
# Размер файла 62 байта: 54 заголовка + 8 байт данных
# Формат: b'BM...'
bmp_bytes = (
b'BM' +
struct.pack('<I', 62) + # размер файла
struct.pack('<HH', 0, 0) + # зарезервировано
struct.pack('<I', 54) + # смещение до данных
struct.pack('<I', 40) + # размер заголовка BITMAPINFOHEADER
struct.pack('<ii', 1, 1) + # ширина и высота
struct.pack('<H', 1) + # planes
struct.pack('<H', 24) + # бит на пиксель
struct.pack('<I', 0) + # сжатие
struct.pack('<I', 8) + # размер изображения
struct.pack('<ii', 2835, 2835) + # разрешение
struct.pack('<I', 0) + # цвета
struct.pack('<I', 0) + # важные цвета
b'\x00\x00\xff' + # RGB: синий пиксель (bytearray)
b'\x00' * 5 # выравнивание до 4 байт
)
# Извлечение ширины и высоты
(width, height) = struct.unpack('<ii', bmp_bytes[18:26])
print(width, height) # 1 11 1
Программа упаковывает заголовок BMP вручную с помощью struct, затем извлекает значения ширины и высоты. Важно соблюдать порядок полей и их размеры.
Создание бинарного протокола с контрольной суммой
import struct
import hashlib
def pack_message(msg_type, payload):
# msg_type: 1 байт, payload: байты, длина 2 байта
length = len(payload)
header = struct.pack('!BH', msg_type, length)
# CRC32 от заголовка и данных
crc = struct.pack('!I', zlib.crc32(header + payload) & 0xFFFFFFFF)
return header + payload + crc
def unpack_message(data):
msg_type, length = struct.unpack('!BH', data[:3])
payload = data[3:3+length]
stored_crc = struct.unpack('!I', data[3+length:3+length+4])[0]
calc_crc = zlib.crc32(data[:3+length]) & 0xFFFFFFFF
if stored_crc != calc_crc:
raise ValueError('Контрольная сумма не совпадает')
return msg_type, payload
import zlib
packet = pack_message(1, b'Hello')
print(packet.hex())
type_, pay = unpack_message(packet)
print(type_, pay)0148656c6c6f2f2e6aaf 1 b'Hello'
Здесь создается пакет с типом (1 байт), длиной (2 байта big-endian), данными и CRC32 (4 байта). Распаковка проверяет целостность. Ошибка - неверно вычисленная длина строки или порядок байтов.