PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

Lire un format binaire en Python avec struct 22

vendredi 26 juin 2015 à 08:51

Une suite de valeurs ne veut rien dire en soi, et même le sacro-saint binaire supposé être le socle de toute l’informatique n’a aucun sens si on ne connaît pas le format utilisé pour ce qu’il doit représenter.

Toujours la même opposition entre données et représentation.

Par exemple, le binaire peut représenter un chiffre en base 2 ou un texte encodé.

Pour autant, cela ne veut pas dire qu’il n’existe pas des formats prépondérant. En informatique, beaucoup de données binaires sont organisées pour correspondre aux structures de données du langage C, ces dernières étant une implémentation du standard IEEE 754 (en effet les strings sont des arrays d’int en C, donc le texte et les nombres sont des suites de chiffres).

Par exemple, si vous créez un array numpy contenant des nombres de 0 à 1000 stockés en int32 et sauvegardez son contenu dans un fichier :

>>> import numpy
>>> numpy.arange(0, 1000, dtype=np.int32).tofile('/tmp/data')

Le fichier va ici contenir une suite de 1 et de 0 représentant 1000 entiers, chacun comme un paquet de 4 octets organisés selon la sémantique que comprend le langage C.

Pour avoir une idée de l’organisation du contenu, on peut prendre un éditeur hexa qui vous affichera :

0000 0000 0100 0000 0200 0000 0300 0000 0400 0000 0500 0000 0600 0000 0700 0000 0800 0000 0900 0000 0a00 0000 0b00 0000 0c00 0000 0d00 0000 0e00 0000 0f00 0000 1000 0000 1100 0000 1200 0000 1300 0000

Ça se lit ainsi :

0000 0000 => 0
0100 0000 => 1
0200 0000 => 2
0300 0000 => 3
0400 0000 => 4
0500 0000 => 5
0600 0000 => 6
0700 0000 => 7
0800 0000 => 8
0900 0000 => 9
0a00 0000 => 10
0b00 0000 => 11
0c00 0000 => 12
0d00 0000 => 13
0e00 0000 => 14
0f00 0000 => 15
1000 0000 => 16
1100 0000 => 17
1200 0000 => 18
1300 0000 => 19
...

Numpy étant codé en C, cela semble plutôt logique qu’il dump tout ça dans ce format.

Mais c’est une représentation tellement courante que de nombreux formats standards l’utilisent. Par exemple, les archives et les images stockent souvent leurs données ainsi.

Prenez le format d’image PNG, la RFC indique que la taille de l’image est stockée dans le fichier sous la forme de deux entiers représentés par 4 octets chacun, ordonnés en big-endian, entre l’octet 16 et l’octet 24.

On peut donc récupérer ces informations en lisant son fichier image :

with open('image.png', 'rb') as f:
    taille = f.read(24)[16:24]

Le problème étant : comment lire cette info ? C’est un blob binaire qui ne veut rien dire pour Python :

print(taille)
b'\x00\x00\x07\x80\x00\x00\x048'

Le module struct est fait pour ça, on lui passe une donnée au format structure C, et il la convertit en type Python. Cela marche ansi, pardon, ainsi :

struct.unpack('motif_du_format_a_convertir', donnee)

Le format à convertir est une chaîne de caractères qui contient des symboles décrivant la structure de la donnée qu’on souhaite récupérer. Little-endian ou big-endian ? String, Int, Bool ?

Pour la taille de la photo, on sait qu’il y a deux entiers, non signés (une taille ne va pas être négative), en big-endian. D’après la doc de struct, on peut lui désigner un entier non signé avec ‘I’, et il faut les qualifier avec ‘>’ pour l’ordre big-endian. Du coup:

taille = struct.unpack('>II', taille)
print(taille)
(1920, 1080)

Il se trouve que mon image de test est un screenshot et que mon écran a une résolution de 1920×1080 :)

On peut faire l’opération inverse avec struct.pack, et bien entendu manipuler des formats plus complexes : il suffit de changer le motif qui représente le format à convertir.

Error happened! 0 - count(): Argument #1 ($value) must be of type Countable|array, null given In: /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php:428 http://ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/?Lire-un-format-binaire-en-Python-avec-struct-1 #0 /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php(999): VroumVroum_Blog->update() #1 /var/www/ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/index.php(1): require_once('...') #2 {main}