2011-08-25 19 views
11

Piszę parser dla formatu binarnego. Ten format binarny obejmuje różne tabele, które są ponownie w formacie binarnym, zwykle zawierające zmienne rozmiary pól (gdzieś pomiędzy 50-100 z nich).Uzyskiwanie dostępu do bitfieldów podczas odczytu/zapisu struktur danych binarnych

Większość z tych obiektów będzie miał bitfields i będzie wyglądać jak te, gdy reprezentowane w katalogu C:

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

natknąłem modułu struct ale sobie sprawę, że jej najniższy rozdzielczości bajt, a nie trochę, inaczej moduł prawie pasował do tej pracy.

Znam bitfields są obsługiwane przy użyciu ctypes, ale nie jestem pewien, jak interfejs ctypes structs zawierających bitfields tutaj.

Moja druga opcja polega na manipulowaniu bitami i dodawaniu ich do bajtów oraz wykorzystaniu ich w module struct - ale ponieważ mam blisko 50-100 różnych typów takich struktur, zapisanie kodu w ten sposób staje się bardziej błędne. skłonny. Martwię się również o wydajność, ponieważ to narzędzie może być używane do analizowania dużych gigabajtów danych binarnych.

Dzięki.

+0

Istnieją również zewnętrzne biblioteki bitowe szyku/bit manipulacji. – agf

+0

To byłaby spora praca, ale prawdopodobnie mógłbyś zaprojektować klasę, która mogłaby parsować definicje struktur w stylu C (lub coś podobnego do nich, które wyeliminowało niejednoznaczność pakowania) do zestawu masek dla każdego pola bitowego, odczytać dane za pośrednictwem moduł struct, aby przejść do poziomu bajtów i zaoferować dostęp do '__getattr__'. –

+0

Tak, teraz natknąłem się na te narzędzia - [python-bitstring] (http://code.google.com/p/python-bitstring/), [Construct] (http://construct.wikispaces.com/tut-basics), [BitReader] (https://bitbucket.org/jtoivola/bitreader/wiki/Home) - i czytanie przez ich dokumenty.Bit Reader wydaje się być dobrym rozwiązaniem, ale widzę [tutaj] (http://blog.mfabrik.com/2010/09/08/bitreader-python-module-for-reading-bits-from-bytes/), że wydajność będzie wielkim hitem. Zbuduj tak daleko, jak mogłem znaleźć w ich podstawowej dokumentacji nie obsługuje pól bitowych. Python-bitstring brzmi obiecująco i trzeba wkopać się głębiej – Tuxdude

Odpowiedz

4

Korzystanie bitstring (które można wymienić patrzysz) powinno być dość łatwe do wdrożenia. Po pierwsze, aby utworzyć niektóre dane do dekodowania:

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

A potem dekodowanie go ponownie tylko

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

Twoim głównym problemem może okazać się szybkość. Biblioteka jest dobrze zoptymalizowana w Pythonie, ale nie jest tak szybka, jak mogłaby być biblioteka C.

+0

Dzięki Scott - tak, sprawdziłem twoją bibliotekę bitstring i naprawdę bardzo zbliżyłem się do moich wymagań. W rzeczywistości opublikowałem pytanie na liście dyskusyjnej [tutaj] (http://groups.google.com/group/python-bitstring/browse_thread/thread/2d85a909aab9d818?tvc=2). Rozumiem, że można go odczytać jako listę - ale chciałbym raczej użyć słownika tylko dla wygody odczytu kodu, ponieważ struktury, z którymi będę miał do czynienia, będą miały łatwo więcej niż 20 lub 30 pól. Wiem, że jest obsługiwany w pakiecie, ale chciałby wiedzieć, jak go używać z rozpakowaniem, ponieważ będzie to podstawowa funkcjonalność. – Tuxdude

+0

@Ash: Nie można jeszcze rozpakować do słownika. Myślę, że potrzebujesz czegoś takiego jak proponowana metoda dekodowania [tutaj] (http://code.google.com/p/python-bitstring/wiki/EncodeDecode), co nie zostało zrobione częściowo dlatego, że tak naprawdę chciałbym return to uporządkowany słownik - nie jestem pewien, czy nieużyteczny słownik byłby przydatny. Pomyślę o tym trochę więcej ... –

+0

tak, ma sens zwrot zwróconego słownika, ale myślę, że jego obsługa jest obecna bezpośrednio tylko w Pythonie 3.3a0 (lub przynajmniej na podstawie tego, co mówi strona [tutaj - PEP372] (http://docs.python.org/dev/whatsnew/2.7.html) – Tuxdude

6

Nie testowałem tego rygorystycznie, ale wygląda na to, że działa z typami niepodpisanymi (edycja: działa również z podpisanymi bajtami/krótkimi typami).

Edytuj 2: To naprawdę hit lub miss. Zależy to od sposobu, w jaki kompilator biblioteki spakował bity do struktury, która nie jest standaryzowana. Na przykład, z gcc 4.5.3 działa tak długo, dopóki nie użyję atrybutu do spakowania struktury, tj. __attribute__ ((__packed__)) (więc zamiast 6 bajtów zostanie spakowany do 4 bajtów, które można sprawdzić przy pomocy __alignof__ i sizeof). Mogę sprawić, że będzie prawie działać, dodając _pack_ = True do definicji struktury ctypes, ale nie powiedzie się ona dla fieldE. Uwagi gcc: "Przesunięcie pola bitowego" pole bitowe "zmieniło się w 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]))) 

wyjściowa:

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

Zobacz testowany przykład bez potrzeby posiadania jakiegokolwiek kodu C lub biblioteki DLL w [Czy Python ma typ bitfield?] (Http://stackoverflow.com/a/11481471/507544) – nealmcb

+0

@nealmcb - Twój przykład przedstawia sposób do przechowywania takich danych w samym Pythonie. Ale w jaki sposób można importować lub eksportować takie dane z/do strumienia bajtów, które można odczytać/zapisać na dysku, lub można je odzyskać/wysłać przez sieć? – Tuxdude

+0

@ash To jest to, czym jest związek i pole 'flags.asbyte' w tym przykładzie. Dzięki za wskazanie, że nie było tak jasne. Wypolerowałem tam tekst, aby był nieco bardziej przejrzysty. Heh :) – nealmcb