2011-08-25 10 views
11

Sto scrivendo un parser per un formato binario. Questo formato binario coinvolge diverse tabelle che sono di nuovo in formato binario che contengono in genere campi di dimensioni variabili (da qualche parte tra 50 e 100).Accesso ai bitfield durante la lettura/scrittura di strutture di dati binari

La maggior parte di queste strutture avranno campi di bit e sarà simile a questi quando rappresentato in C:

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

mi sono imbattuto il modulo struct, ma sono reso conto che la sua risoluzione più bassa era un byte e non un po ', altrimenti il modulo praticamente era adatto per questo lavoro.

So che i bitfield sono supportati usando i ctypes, ma non sono sicuro di come interfacciare le strutture di tipo ctypes che contengono bitfield qui.

La mia altra opzione è quella di manipolare i bit personalmente e inserirli in byte e utilizzarli con il modulo struct - ma poiché ho vicino a 50-100 tipi diversi di tali strutture, scrivere il codice per questo diventa più un errore- prona. Sono anche preoccupato per l'efficienza poiché questo strumento potrebbe essere utilizzato per analizzare grandi gigabyte di dati binari.

Grazie.

+0

ci sono anche librerie di manipolazione bit array/bit di terze parti. – agf

+0

Sarebbe una buona quantità di lavoro, ma probabilmente potresti progettare una classe in grado di analizzare le definizioni di struttura in stile C (o qualcosa di simile a loro che eliminava l'ambiguità di imballaggio) in un set di maschere per ogni bitfield, leggi i dati in via il modulo struct per arrivare a livello di byte e offrire l'accesso '__getattr__'. –

+0

Sì, mi sono imbattuto in questi strumenti: [python-bitstring] (http://code.google.com/p/python-bitstring/), [Construct] (http://construct.wikispaces.com/tut-basics), [BitReader] (https://bitbucket.org/jtoivola/bitreader/wiki/Home) - e leggendo i loro documenti.Bit Reader sembra una soluzione valida ma vedo [qui] (http://blog.mfabrik.com/2010/09/08/bitreader-python-module-for-reading-bits-from-bytes/) che la performance sarà un grande successo. Costruire per quanto ho potuto trovare dalla loro documentazione di base non supporta campi di bit. Python-bitstring sembra promettente e ha bisogno di scavare un po 'più in profondità – Tuxdude

risposta

4

Utilizzando bitstring (che si parla si sta guardando) dovrebbe essere abbastanza facile da implementare. Prima di creare alcuni dati da decodificare:

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

E poi la decodifica di nuovo è solo

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

vostra preoccupazione principale potrebbe essere la velocità. La libreria è ben ottimizzata per Python, ma non è così veloce come una libreria C.

+0

Grazie Scott - sì, ho controllato la libreria bitstring e mi è molto vicina alle mie esigenze. Infatti ho postato la domanda nella mailing list [qui] (http://groups.google.com/group/python-bitstring/browse_thread/thread/2d85a909aab9d818?tvc=2). Posso capire che può essere letto come una lista - ma mi piacerebbe preferibilmente usare un dizionario solo per comodità di leggibilità del codice, dal momento che le strutture con le quali avrei a che fare avrebbero più di 20 o 30 campi facilmente. So che è supportato in pack, ma vorrei sapere come usarlo con lo spacchettamento poiché questa sarà la funzionalità principale. – Tuxdude

+0

@Ash: non è ancora possibile decomprimere in un dizionario. Penso che tu abbia bisogno di qualcosa come il metodo di decodifica proposto [qui] (http://code.google.com/p/python-bitstring/wiki/EncodeDecode), che non è stato fatto in parte perché quello che mi piacerebbe davvero return è un dizionario ordinato - non sono sicuro che un dizionario non ordinato sarebbe così utile. Ci penserò ancora un po '... –

+0

sì, ha senso restituire un dizionario ordinato ma immagino che il supporto sia presente direttamente solo in Python 3.3a0 (o almeno in base a ciò che dice la pagina [qui- PEP372] (http://docs.python.org/dev/whatsnew/2.7.html) – Tuxdude

6

Non l'ho testato rigorosamente, ma sembra funzionare con tipi senza segno (modifica: funziona anche con byte/tipi con segno).

Modifica 2: Questo è davvero un errore. Dipende dal modo in cui il compilatore della libreria ha inserito i bit nella struttura, che non è standardizzata. Ad esempio, con gcc 4.5.3 funziona fintanto che non utilizzo l'attributo per impacchettare la struct, ovvero __attribute__ ((__packed__)) (quindi anziché 6 byte viene compresso in 4 byte, che è possibile verificare con __alignof__ e sizeof). Posso farlo funzionare quasi aggiungendo _pack_ = True alla definizione Struttura ctypes, ma non riesce per fieldE. Note gcc: "Offset di campo di bit compresso" fieldE "è cambiato in 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]))) 

uscita:

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

Vedere un esempio testato senza la necessità di alcun codice C o DLL in [Python ha un tipo bitfield?] (Http://stackoverflow.com/a/11481471/507544) – nealmcb

+0

@nealmcb - Il tuo esempio rappresenta un modo per memorizzare tali dati all'interno di Python stesso. Ma come importate o esportate tali dati da/a un flusso di byte che può essere letto/scritto su disco o può essere recesso/inviato tramite rete? – Tuxdude

+0

@ash Ecco a cosa serve l'unione e il campo 'flags.asbyte' in quell'esempio. Grazie per aver sottolineato che non era così chiaro. Ho lucidato il testo lì per renderlo un po 'più chiaro. Heh :) – nealmcb

Problemi correlati