我使用ctypes位字段来剖析紧密压缩的二进制数据。我将一个记录的数据作为字符串填充到一个联合中,然后将关键字段作为整数拉出。
当缓冲区中没有空值时,这很有用,但任何嵌入的空值都会导致细胞截断字符串。
示例:
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源代码,我看到两个设置char数组的方法,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 :(得分:0)
c_char*6
被处理为以null结尾的字符串。切换到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)
答案 1 :(得分:0)
您还可以在struct / union之外创建原始字符串数组:
mystring = (c_char * 6).from_buffer(u2)
print mystring.raw
这样您就不会有任何转化费用。 我想知道为什么(c_char * 6)在单独使用时表现不同而在结构/联盟中使用...