Conseils pour gérer les binaires en Python

Voici quelques conseils pour travailler avec des binaires en Python.

Il existe deux façons de travailler avec des binaires en Python, le module struct et la classe ctypes.Structure. Fondamentalement, le module struct utilise la classe ctypes.Structure lorsque vous souhaitez gérer quelques octets de binaire, ou lorsque vous souhaitez travailler avec plus d'octets ou C / C ++.

module struct

A titre d'exemple, lisons le binaire d'un fichier PNG. Dans un fichier PNG, les 8 premiers octets sont fixés dans l'en-tête. Les 9e à 18e octets de données sont stockés dans la zone IHDR (pour être exact, une partie de l'IHDR), la taille verticale et horizontale de l'image, la profondeur de bits et le mode couleur.

import struct

png_data = open("sample.png ", "rb").read()

struct.unpack_from(">I4sIIBB", png_data, 8)
# (13, b'IHDR', 250, 156, 8, 2)

Vous pouvez lire les données avec struct.unpack, mais vous obtiendrez une erreur si le décalage et la taille du tampon que vous donnez ne sont pas exactement les mêmes. Struct.unpack_from est utile si vous voulez lire une partie des données.

Mettre est x

Lors de la lecture du binaire, la mise (zone de poussière pour l'alignement) sort par tous les moyens. Le format «x» est pratique car il ignore les données.

data = b'd\x00\xb0\x04'

# NG
kind, _, value = struct.unpack("BBH", data)

# Yes!
kind, value = struct.unpack("BxH", data)

classe struct.Struct

La classe struct.Struct est une classification de la chaîne de format du module struct. Puisque le format est analysé lorsque la classe est instanciée, il est plus rapide de créer l'instance à l'avance lorsque pack / ʻunpackà plusieurs reprises dans la boucle. C'est déroutant avec la classectypes.Structre`.

point = struct.Struct("HH")

for x, y in zip(range(10), range(10)):
    point.pack(x, y)

Liste des caractères de format

lettre Type de langage C Taille standard
x Mettre du mordant 1
c char 1
b signed char 1
B unsigned char, BYTE 1
? _Bool 1
h short 2
H unsinged short, WORD 2
i int 4
I unsigned int, DWORD 4
l long, LONG 4
L unsigned long, ULONG 4
q long long, LONGLONG 8
Q unsigned long long, ULONGLONG 8
n ssize_t(Python3.3 ou plus tard) Native uniquement
N size_t(Python3.3 ou plus tard) Native uniquement
f float 4
d double 8
s char[] -
p char[] -
P void * -

Exemple de caractère de format:

Structure de BITMAPINFOHEADER


typedef struct tagBITMAPINFOHEADER {
    DWORD  biSize;
    LONG   biWidth;
    LONG   biHeight;
    WORD   biPlanes;
    WORD   biBitCount;
    DWORD  biCompression;
    DWORD  biSizeImage;
    LONG   biXPelsPerMeter;
    LONG   biYPelsPerMeter;
    DWORD  biClrUsed;
    DWORD  biClrImportant;
} BITMAPINFOHEADER;

Format des caractères pour la structure BITMAPINFOHEADER


"IllHHIIllII"

Liste de l'ordre et de l'alignement des octets

lettre Ordre des octets Taille alignement
@ Native Native Native
= Native Taille standard Aucun
< Petit indien Taille standard Aucun
> Big endian Taille standard Aucun
! Big endian Taille standard Aucun

@Quand=La différence de(CPU=amd64,OS=Ubuntu64bit)


struct.calcsize("BI")
# 8

struct.calcsize("=BI")
# 5

Notez que si vous spécifiez explicitement l'endian, l'alignement sera "aucun".

ctypes.Structure classe

Vous pouvez travailler avec des structures C / C ++ dans la classe ctypes.Structure. Si vous essayez de lire beaucoup de données avec le module'struct ', le format sera comme un sort, donc si vous voulez écrire fermement beaucoup de données binaires en lecture, vous devriez utiliser la classe ctypes.Structure. Faisons le.

Bases de la structure

Hériter de ctypes.Structure et définir les types dans _field_.

from ctypes import *

"""
typedef struct {
    char identity[4];
    uint16_t x;
    uint16_t y;
} TestStructure;
"""
class TestStructure(Structure):
    _fields_ = (
        ('identity', c_char * 4),
        ('x', c_uint16),
        ('y', c_uint16),
    )

L'instance est définie comme suit.

t = TestStructure(b"TEST", 100, 100)