Buscando / leyendo datos binarios en Python

Estoy leyendo en un archivo binario (un jpg en este caso), y necesito encontrar algunos valores en ese archivo. Para aquellos interesados, el archivo binario es un jpg y estoy intentando seleccionar sus dimensiones buscando la estructura binaria que se detalla aquí .

Necesito encontrar FFC0 en los datos binarios, saltar un cierto número de bytes y luego leer 4 bytes (esto debería darme las dimensiones de la imagen).

¿Cuál es una buena manera de buscar el valor en los datos binarios? ¿Hay un equivalente de ‘encontrar’, o algo así como re?

Realmente podría cargar el archivo en una cadena y buscar esa secuencia para la secuencia de bytes 0xffc0 usando el método str.find() . Funciona para cualquier secuencia de bytes.

El código para hacer esto depende de un par de cosas. Si abre el archivo en modo binario y está usando Python 3 (ambos son probablemente la mejor práctica para este escenario), deberá buscar una cadena de bytes (en lugar de una cadena de caracteres), lo que significa que Tiene que prefijar la cadena con b .

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

Si abres el archivo en modo texto en Python 3, deberías buscar una cadena de caracteres:

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

aunque no hay ninguna razón particular para hacer esto. No le brinda ninguna ventaja sobre la forma anterior, y si se encuentra en una plataforma que trata los archivos binarios y de texto de manera diferente (por ejemplo, Windows), existe la posibilidad de que esto cause problemas.

Python 2 no hace la distinción entre cadenas de bytes y cadenas de caracteres, por lo que si está usando esa versión, no importa si incluye o excluye la b en b'\xff\xc0' . Y si su plataforma trata los archivos binarios y los archivos de texto de manera idéntica (por ejemplo, Mac o Linux), no importa si usa 'r' o 'rb' como modo de archivo. Pero aún así, recomiendo usar algo como el primer ejemplo de código anterior solo por compatibilidad hacia adelante, en caso de que alguna vez cambie a Python 3, es una cosa menos que solucionar.

El módulo de cadena de bits fue diseñado para casi este propósito. Para su caso, el siguiente código (que no he probado) debería ayudar a ilustrar:

 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)) 

En lugar de leer todo el archivo en la memoria, buscarlo y luego escribir un nuevo archivo en el disco, puede usar el módulo mmap para esto. mmap no almacenará el archivo completo en la memoria y permite la modificación en el lugar.

 #!/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') 

El módulo re funciona con cadenas y datos binarios ( str en Python 2 y bytes en Python 3), por lo que puede usarlo así como str.find para su tarea.

Bueno, obviamente hay PIL. El módulo de imagen tiene tamaño como atributo. Si desea obtener el tamaño exactamente como sugiere y sin cargar el archivo, tendrá que analizarlo línea por línea. No es la mejor manera de hacerlo pero funcionaría.

El método find() debe usarse solo si necesita conocer la posición de sub, si no, puede usar el operador in , por ejemplo:

 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!') 

En Python 3.x puede buscar una cadena de bytes por otra cadena de bytes como esta:

 >>> 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 >>> 

Para 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