2011-08-25 11 views
11

Estoy escribiendo un analizador para un formato binario. Este formato binario involucra diferentes tablas que están de nuevo en formato binario con tamaños de campo variables generalmente (en algún lugar entre 50 - 100 de ellos).Accediendo a los campos de bit mientras lee/escribe estructuras de datos binarios

La mayoría de estas estructuras tendrán campos de bits y se verá algo como esto cuando se representa en C:

struct myHeader 
{ 
    unsigned char fieldA : 3 
    unsigned char fieldB : 2; 
    unsigned char fieldC : 3; 
    unsigned short fieldD : 14; 
    unsigned char fieldE : 4 
} 

me encontré con el módulo de estructura pero se dio cuenta de que su resolución más baja fue de un byte y no un poco, de lo contrario el módulo fue el más adecuado para este trabajo.

Sé que los bitfields son compatibles con ctypes, pero no estoy seguro cómo interconectar las estructuras de ctypes que contienen bitfields aquí.

Mi otra opción es manipular los bits yo mismo y alimentarlo en bytes y usarlo con el módulo struct, pero dado que tengo cerca de 50-100 tipos diferentes de tales estructuras, escribir el código para eso se convierte en más error- propenso. También me preocupa la eficiencia, ya que esta herramienta podría usarse para analizar grandes gigabytes de datos binarios.

Gracias.

+0

También hay bibliotecas de manipulación de bits/matriz de bits. – agf

+0

Sería una buena cantidad de trabajo, pero probablemente podría diseñar una clase que pueda analizar las definiciones de estructura tipo C (o algo similar a ellas que eliminó la ambigüedad de empaque) en un conjunto de máscaras para cada campo de bits, leer los datos en via el módulo struct para llegar al nivel de bytes, y ofrecer el acceso '__getattr__'. –

+0

Sí, ahora encontré estas herramientas - [python-bitstring] (http://code.google.com/p/python-bitstring/), [Construir] (http://construct.wikispaces.com/tut-basics), [BitReader] (https://bitbucket.org/jtoivola/bitreader/wiki/Home) - y leyendo sus documentos.Bit Reader parece ser una solución viable, pero veo [aquí] (http://blog.mfabrik.com/2010/09/08/bitreader-python-module-for-reading-bits-from-bytes/) que el rendimiento va a ser un gran éxito. Construir tan lejos como pude encontrar a partir de su documentación básica no admite campos de bits. Python-bitstring suena prometedor y necesita profundizar un poco más – Tuxdude

Respuesta

4

Usando bitstring (que usted menciona que usted está buscando en) que debería ser bastante fácil de implementar. En primer lugar para crear algunos datos para decodificar:

>>> myheader = "3, 2, 3, 14, 4" 
>>> a = bitstring.pack(myheader, 1, 0, 5, 1000, 2) 
>>> a.bin 
'00100101000011111010000010' 
>>> a.tobytes() 
'%\x0f\xa0\x80' 

Y entonces la decodificación de nuevo es sólo

>>> a.readlist(myheader) 
[1, 0, 5, 1000, 2] 

Su principal preocupación podría ser la velocidad. La biblioteca está bien optimizada Python, pero eso no es tan rápido como lo sería una biblioteca C.

+0

Gracias Scott - sí, he revisado tu biblioteca de cadenas de bits y realmente se acerca mucho a mis requisitos. De hecho, publiqué la pregunta en la lista de correo [aquí] (http://groups.google.com/group/python-bitstring/browse_thread/thread/2d85a909aab9d818?tvc=2). Puedo entender que se puede leer como una lista, pero me gustaría usar un diccionario preferiblemente solo para la conveniencia de la legibilidad del código, ya que las estructuras con las que trataré tendrían más de 20 o 30 campos fácilmente. Sé que es compatible con el paquete, pero me gustaría saber cómo usarlo con desempaquetar, ya que esa será la funcionalidad principal. – Tuxdude

+0

@Ash: aún no puedes descomprimir en un diccionario. Creo que necesitas algo como el método de descodificación propuesto [aquí] (http://code.google.com/p/python-bitstring/wiki/EncodeDecode), que no se ha hecho en parte porque lo que realmente me gustaría return es un diccionario ordenado; no estoy seguro de que un diccionario desordenado sea tan útil. Lo pensaré un poco más ... –

+0

sí, tiene sentido devolver un diccionario ordenado, pero supongo que su soporte está presente directamente solo en Python 3.3a0 (o al menos en función de lo que dice la página [aquí- PEP372] (http://docs.python.org/dev/whatsnew/2.7.html) – Tuxdude

6

No lo he probado rigurosamente, pero parece funcionar con tipos sin signo (editar: también funciona con tipos de bytes firmados/cortos).

Editar 2: Esto es realmente impredecible. Depende de la forma en que el compilador de la biblioteca empaquetó los bits en la estructura, que no está estandarizada. Por ejemplo, con gcc 4.5.3 funciona siempre que no use el atributo para empaquetar la estructura, es decir, __attribute__ ((__packed__)) (así que en lugar de 6 bytes se empaqueta en 4 bytes, que puede consultar con __alignof__ y sizeof). Puedo hacer que funcione casi añadiendo _pack_ = True a la definición de Estructura de ctypes, pero falla para fieldE. gcc notes: "El desplazamiento del campo de bits empaquetado 'fieldE' ha cambiado en GCC 4.4".

import ctypes 

class MyHeader(ctypes.Structure): 
    _fields_ = [ 
     ('fieldA', ctypes.c_ubyte, 3), 
     ('fieldB', ctypes.c_ubyte, 2), 
     ('fieldC', ctypes.c_ubyte, 3), 
     ('fieldD', ctypes.c_ushort, 14), 
     ('fieldE', ctypes.c_ubyte, 4), 
    ] 

lib = ctypes.cdll.LoadLibrary('C/bitfield.dll') 

hdr = MyHeader() 
lib.set_header(ctypes.byref(hdr)) 

for x in hdr._fields_: 
    print("%s: %d" % (x[0], getattr(hdr, x[0]))) 

Salida:

fieldA: 3 
fieldB: 1 
fieldC: 5 
fieldD: 12345 
fieldE: 9 

C:

typedef struct _MyHeader { 
    unsigned char fieldA : 3; 
    unsigned char fieldB : 2; 
    unsigned char fieldC : 3; 
    unsigned short fieldD : 14; 
    unsigned char fieldE : 4; 
} MyHeader, *pMyHeader; 

int set_header(pMyHeader hdr) { 

    hdr->fieldA = 3; 
    hdr->fieldB = 1; 
    hdr->fieldC = 5; 
    hdr->fieldD = 12345; 
    hdr->fieldE = 9; 

    return(0); 
} 
+0

Vea un ejemplo probado sin la necesidad de ningún código C o dlls en absoluto en [Does Python tiene un tipo de campo de bits?] (Http://stackoverflow.com/a/11481471/507544) – nealmcb

+0

@nealmcb - Su ejemplo representa una forma para almacenar dichos datos dentro de Python. Pero, ¿cómo se importan o exportan dichos datos de/a una secuencia de bytes que se pueden leer/escribir en el disco o se pueden recibir o enviar por la red? – Tuxdude

+0

@ash Para eso está la unión, y el campo 'flags.asbyte' en ese ejemplo. Gracias por señalar que no estaba tan claro. He pulido el texto allí para hacerlo un poco más claro. Heh :) – nealmcb

Cuestiones relacionadas