2014-10-09 103 views
1

我使用ctypes位字段來解剖緊密壓縮的二進制數據。我將一條記錄的數據作爲一個字符串填充到一個聯合中,然後將整個關鍵字段提取出來。Python ctypes - 當字符串嵌入null時設置c_char數組?

如果緩衝區中沒有空值,但任何嵌入的空值都會導致cytpes截斷字符串,則此功能非常有用。

實施例:

from ctypes import * 

class H(BigEndianStructure): 
    _fields_ = [ ('f1', c_int, 8), 
       ('f2', c_int, 8), 
       ('f3', c_int, 8), 
       ('f4', c_int, 2) 
       # ... 
       ] 

class U(Union): 
    _fields_ = [ ('fld', H), 
       ('buf', c_char * 6) 
       ] 

# With no nulls, works as expected... 
u1 = U() 
u1.buf='abcabc' 
print '{} {} {} (expect: 97 98 99)'.format(u1.fld.f1, u1.fld.f2, u1.fld.f3) 

# Embedded null breaks it... This prints '97 0 0', NOT '97 0 99' 
u2 = U() 
u2.buf='a\x00cabc' 
print '{} {} {} (expect: 97 0 99)'.format(u2.fld.f1, u2.fld.f2, u2.fld.f3) 

瀏覽ctypes的源,我看到兩種方法設置一個字符數組,CharArray_set_value()和CharArray_set_raw()。看起來CharArray_set_raw()將正確處理空值,而CharArray_set_value()則不會。

但我無法弄清楚如何調用原始版本......它看起來像一個屬性,所以我希望是這樣的:

ui.buf.raw = 'abcabc' 

但收益率:

AttributeError: 'str' object has no attribute raw 

任何指導讚賞。 (包括完全不同的方法!)

(注意:我需要每秒處理數千條記錄,所以效率非常關鍵,使用數組理解在結構體中填充字節數組,但速度要慢100倍。

回答

0

c_char*6不幸被作爲一個nul結尾的字符串處理。切換到c_byte*6代替,但失去了與字符串初始化的方便:

from ctypes import * 

class H(BigEndianStructure): 
    _fields_ = [ ('f1', c_int, 8), 
       ('f2', c_int, 8), 
       ('f3', c_int, 8), 
       ('f4', c_int, 2) 
       # ... 
       ] 

class U(Union): 
    _fields_ = [ ('fld', H), 
       ('buf', c_byte * 6) 
       ] 

u1 = U() 
u1.buf=(c_byte*6)(97,98,99,97,98,99) 
print '{} {} {} (expect: 97 98 99)'.format(u1.fld.f1, u1.fld.f2, u1.fld.f3) 

u2 = U() 
u2.buf=(c_byte*6)(97,0,99,97,98,99) 
print '{} {} {} (expect: 97 0 99)'.format(u2.fld.f1, u2.fld.f2, u2.fld.f3) 

輸出:

97 98 99 (expect: 97 98 99) 
97 0 99 (expect: 97 0 99) 
+0

謝謝,馬克。這有效,但與將字符串中的字節整理到字節數組相關聯的CPU開銷使得我的應用程序的方法過於緩慢。 (我的試用比ctype的memcpy()慢100倍)。 – Simian 2014-10-13 17:20:15

0

您還可以創建您的結構/聯合外的原始字符串數組:

mystring = (c_char * 6).from_buffer(u2) 
print mystring.raw 

這樣你就沒有任何轉換開銷。 我不知道爲什麼(c_char * 6)在單獨使用時與使用在結構/聯合中時表現不同?

+0

爲了方便起見(通常),'c_char'和'c_wchar'數組的'CField'描述符是在'PyCField_FromDesc'中(在Modules/_ctypes/cfield.c中)專用的,用於使用's_get '/'s_set'和'U_get' /'U_set'。 – eryksun 2015-12-11 14:17:56

+1

不要爲此使用'from_address',因爲結果數組沒有在源緩衝區'u2'上擁有引用。這是段錯誤災難的祕訣。使用'(c_char * 6).from_buffer(u2)'。 – eryksun 2015-12-11 14:23:04

+0

在這種情況下,我更願意將字段名稱設置爲私有(例如'_buf')並使用公共屬性。 – eryksun 2015-12-11 14:24:48