web-dev-qa-db-fra.com

Recherche/lecture de données binaires en Python

Je lis dans un fichier binaire (un jpg dans ce cas), et j'ai besoin de trouver des valeurs dans ce fichier. Pour ceux que cela intéresse, le fichier binaire est un fichier jpg et j'essaie de choisir ses dimensions en recherchant la structure binaire telle que détaillée ici

Je dois trouver FFC0 dans les données binaires, sauter un certain nombre d'octets, puis lire 4 octets (cela devrait me donner les dimensions de l'image).

Quel est un bon moyen de rechercher la valeur dans les données binaires? Existe-t-il un équivalent de 'trouver' ou quelque chose comme re?

20
Parand

Vous pouvez réellement charger le fichier dans une chaîne et rechercher cette séquence d'octets 0xffc0 à l'aide de la méthode str.find(). Cela fonctionne pour n'importe quelle séquence d'octets.

Le code pour ce faire dépend de deux choses. Si vous ouvrez le fichier en mode binaire et que vous utilisez Python 3 (ce qui constitue probablement l'une des meilleures pratiques pour ce scénario), vous devrez rechercher une chaîne d'octets (par opposition à une chaîne de caractères), ce qui signifie que vous devez préfixer la chaîne avec b.

with open(filename, 'rb') as f:
    s = f.read()
s.find(b'\xff\xc0')

Si vous ouvrez le fichier en mode texte dans Python 3, vous devrez rechercher une chaîne de caractères:

with open(filename, 'r') as f:
    s = f.read()
s.find('\xff\xc0')

bien qu'il n'y ait aucune raison particulière de le faire. Cela ne vous procure aucun avantage par rapport à la méthode précédente, et si vous êtes sur une plate-forme qui traite les fichiers binaires et les fichiers texte de manière différente (Windows, par exemple), il est possible que cela cause des problèmes.

Python 2 ne fait pas la distinction entre les chaînes d'octets et les chaînes de caractères. Par conséquent, si vous utilisez cette version, peu importe que vous incluiez ou non la b dans b'\xff\xc0'. Et si votre plate-forme traite les fichiers binaires et les fichiers texte de la même manière (par exemple, Mac ou Linux), peu importe que vous utilisiez 'r' ou 'rb' comme mode de fichier. Mais je recommanderais quand même d’utiliser quelque chose comme le premier exemple de code ci-dessus, juste pour assurer la compatibilité ascendante - au cas où vous basculeriez en Python 3, c’est une solution de moins.

10
David Z

Le module bitstring a été conçu à cette fin. Pour votre cas, le code suivant (que je n'ai pas testé) devrait aider à illustrer:

from bitstring import ConstBitStream
# Can initialise from files, bytes, etc.
s = ConstBitStream(filename='your_file')
# Search to Start of Frame 0 code on byte boundary
found = s.find('0xffc0', bytealigned=True)
if found:
    print("Found start code at byte offset %d." % found[0])
    s0f0, length, bitdepth, height, width = s.readlist('hex:16, uint:16, 
                                                        uint:8, 2*uint:16')
    print("Width %d, Height %d" % (width, height))
6
Scott Griffiths

Au lieu de lire le fichier entier en mémoire, de le rechercher puis de l'écrire sur le disque, vous pouvez utiliser le module mmap pour cela. mmap conservera pas tout le fichier en mémoire et permettra une modification sur place. 

#!/usr/bin/python

import mmap

with open("hugefile", "rw+b") as f:
    mm = mmap.mmap(f.fileno(), 0)
    print mm.find('\x00\x09\x03\x03')
5
synthesizerpatel

Le module re fonctionne fonctionne à la fois avec string et binary data (str en Python 2 et bytes en Python 3), afin que vous puissiez l’utiliser ainsi que str.find pour votre tâche.

4
Andrey Vlasovskikh

Bien évidemment, il y a PIL Le module Image a la taille comme attribut. Si vous souhaitez obtenir la taille exactement comme vous le suggérez et sans charger le fichier, vous devrez le parcourir ligne par ligne. Pas le meilleur moyen de le faire, mais ça marcherait.

2
fridder

La méthode find() ne doit être utilisée que si vous devez connaître la position de sub, sinon vous pouvez utiliser l'opérateur in, par exemple:

with open("foo.bin", 'rb') as f:
    if b'\x00' in f.read():
        print('The file is binary!')
    else:
        print('The file is not binary!')
1
kenorb

Dans Python 3.x, vous pouvez rechercher une chaîne d'octets à l'aide d'une autre chaîne d'octets comme celle-ci:

>>> byte_array = b'this is a byte array\r\n\r\nXYZ\x80\x04\x95 \x00\x00\x00\x00\x00'
>>> byte_array.find('\r\n\r\n'.encode())
20
>>>
1
caleb

Pour Python> = 3.2:

import re

f = open("filename.jpg", "rb")
byte = f.read()
f.close()

matchObj = re.match( b'\xff\xd8.*\xff\xc0...(..)(..).*\xff\xd9', byte, re.MULTILINE|re.DOTALL)
if matchObj:
    # http://stackoverflow.com/questions/444591/convert-a-string-of-bytes-into-an-int-python
    print (int.from_bytes(matchObj.group(1), 'big')) # height
    print (int.from_bytes(matchObj.group(2), 'big')) # width
0
kissson