2010-07-10 25 views
13

Estoy leyendo en un archivo binario (un jpg en este caso), y necesito encontrar algunos valores en ese archivo. Para los interesados, el archivo binario es un jpg y estoy tratando de elegir sus dimensiones buscando la estructura binaria como detailed here.Python: Búsqueda/lectura de datos binarios

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

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

+1

¿Alguna vez has mirado en imagick? IIRC también hay una biblioteca de pitones para ello. – txwikinger

+0

Tengo, y funciona muy bien, pero es bastante pesado solo para encontrar las dimensiones del archivo. – Parand

+1

debe usar un módulo apropiado para algo como esto http://snippets.dzone.com/posts/show/1021 –

Respuesta

7

En realidad, podría cargar el archivo en una cadena y buscar esa cadena para la secuencia de bytes 0xffc0 utilizando 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á utilizando Python 3 (probablemente ambas mejores prácticas para este escenario), necesitará buscar una cadena de bytes (en lugar de una cadena de caracteres), lo que significa que tiene que ponerle un prefijo a la cadena con b.

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

Si abre el archivo en modo de texto en Python 3, habría que buscar una cadena de caracteres:

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

aunque no hay ninguna razón especial para hacer esto. No obtiene ninguna ventaja sobre la forma anterior, y si se encuentra en una plataforma que trata los archivos binarios y los archivos 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 caracteres y cadenas de caracteres, por lo que si está utilizando esa versión, no importa si incluye o excluye el 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 el modo de archivo. Pero aún recomendaría utilizar algo así como el primer ejemplo de código anterior solo para la compatibilidad con versiones anteriores: en caso de que alguna vez cambie a Python 3, es una cosa menos para solucionar.

+6

Si se trata de un archivo realmente grande, no es una buena idea leerlo en una cadena de una sola vez. – icktoofay

+2

Dudo que sea tan grande que va a ser un problema. –

+3

Como solo estoy buscando el primer fotograma, es probable que pueda leer una pequeña parte del archivo y procesarlo en lugar de leer todo el archivo. – Parand

4

El módulo de rehace trabajo tanto con cadena y datos binarios (str en Python 2 y bytes en Python 3), por lo que se puede utilizar, así como str.find para su tarea.

2

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

6

El módulo bitstring fue diseñado para prácticamente 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)) 
+0

Entonces 'Bits.find' devuelve solo un booleano y establece el atributo' Bits.bytepos'? Quizás en la documentación del módulo debe advertir que 'bitstring' no es seguro para subprocesos (no es que importe en esta respuesta, por supuesto). – tzot

+0

@ ΤΖΩΤΖΙΟΥ: Sí, tiene un buen punto. No me parece sorprendente que los métodos de mutación o los métodos de lectura no sean seguros para la ejecución de subprocesos, pero se podría esperar razonablemente que se use 'encontrar' en un objeto inmutable a modo de bit. Para ser sincero, nunca apareció antes, pero es algo en lo que pensar ... –

+0

Solo una idea: 'find' podría devolver un objeto con toda la información necesaria, à la' re.match' y 're.search'. Podrías hacer que esta clase "BitMatch" sea una subclase de 'bool', para compatibilidad con versiones anteriores. – tzot

1

En lugar de leer todo el archivo en memoria, búsqueda de ella y luego escribir un nuevo archivo al disco se puede utilizar el mmap módulo para esto. mmap will not almacena todo el archivo en la memoria y permite la modificación in situ.

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

pitón> = 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 
1

El método find() debe usarse sólo si es necesario conocer la posición de la sub, si no, puede utilizar 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!') 
+1

Esto lo hizo por mí, estaba tratando de comparar una cadena con una cadena de bytes. Todo lo que tuve que hacer fue poner la b en frente de mi término de búsqueda y se encontró dentro de la cadena de bytes. – pa1983

0

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

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