在Python中实现C的枚举和联合体
我正在研究一些C语言的代码,想把它移植到Python中。这段代码是用来读取一种专有的二进制数据文件格式。到目前为止,这个过程还算简单,主要是用到结构体(struct),我一直在用struct
库从文件中提取特定的数据类型。不过,我遇到了一段代码,搞得我有点迷茫,不知道怎么在Python中实现。特别是,我不太清楚enum
和union
该怎么处理。
#define BYTE char
#define UBYTE unsigned char
#define WORD short
#define UWORD unsigned short
typedef enum {
TEEG_EVENT_TAB1=1,
TEEG_EVENT_TAB2=2
} TEEG_TYPE;
typedef struct
{
TEEG_TYPE Teeg;
long Size;
union
{
void *Ptr; // Memory pointer
long Offset
};
} TEEG;
其次,在下面的结构体定义中,我不太明白变量名后面的冒号是什么意思,比如KeyPad:4
。这是不是意味着我需要读取4个字节的数据?
typedef struct
{
UWORD StimType;
UBYTE KeyBoard;
UBYTE KeyPad:4;
UBYTE Accept:4;
long Offset;
} EVENT1;
如果有用的话,我在Python中访问文件的一个抽象示例如下:
from struct import unpack, calcsize def get(ctype, size=1): """Reads and unpacks binary data into the desired ctype.""" if size == 1: size = '' else: size = str(size) chunk = file.read(calcsize(size + ctype)) return unpack(size + ctype, chunk)[0] file = open("file.bin", "rb") file.seek(1234) var1 = get('i') var2 = get('4l') var3 = get('10s')
4 个回答
C语言中的enum
声明其实就是一种语法上的包装,用来表示一些整数类型。具体来说,enum
的大小取决于你使用的C编译器。一般来说,我建议你可以先尝试16位的大小。
union
则是为包含的最大数据类型预留了一块内存。它的具体大小同样取决于C的实现,但如果是在32位架构下,我预计它会是32位,如果是编译成64位的代码,那就是64位。总的来说,无论union
里存的是指针还是偏移量,你都可以把它的内容存储到Python的整数或长整型中。
一个更有趣的问题是,为什么指针会被写入到磁盘文件中。你可能会发现,union
字段在内存中只被当作指针处理,但写入磁盘时,它总是一个整数偏移量。
至于:4的表示法,正如很多人提到的,这些是“位域”,意思是一些位的序列,多个位可以打包到一个空间里。如果我没记错的话,C语言中的位域会被打包成int
类型,所以这两个4位的位域会被打包成一个整数。你可以通过Python的“&”(按位与)和“>>”(右移)运算符来解包它们。同样,位域是如何打包到整数中的,以及整数字段本身的大小,也会依赖于具体的C实现。
也许下面的代码片段能对你有所帮助:
SIZEOF_TEEG_TYPE = 2 # First guess for enum is two bytes
FMT_TEEG_TYPE = "h" # Could be "b", "B", "h", "H", "l", "L", "q" or "Q"
SIZEOF_LONG = 4 # Use 8 in 64-bit Unix architectures
FMT_LONG = "l" # Use "q" in 64-bit Unix architectures
# Life gets more interesting if you are reading 64-bit
# using 32-bit Python
SIZEOF_PTR_LONG_UNION = 4 # Use 8 in any 64-bit architecture
FMT_PTR_LONG_UNION = "l" # Use "q" in any 64-bit architecture
# Life gets more interesting if you are reading 64-bit
# using 32-bit Python
SIZEOF_TEEG_STRUCT = SIZEOF_TEEG_TYPE + SIZEOF_LONG + SIZEOF_PTR_LONG_UNION
FMT_TEEG_STRUCT = FMT_TEEG_TYPE + FMT_LONG + FMT_PTR_LONG_UNION
# Constants for TEEG_EVENTs
TEEG_EVENT_TAB1 = 1
TEEG_EVENT_TAB2 = 2
.
.
.
# Read a TEEG structure
teeg_raw = file_handle.read( SIZEOF_TEEG_STRUCT )
teeg_type, teeg_size, teeg_offset = struct.unpack( FMT_TEEG_STRUCT, teeg_raw )
.
.
.
# Use TEEG_TYPE information
if teeg_type == TEEG_EVENT_TAB1:
Do something useful
elif teeg_type == TEEG_EVENT_TAB2:
Do something else useful
else:
raise ValueError( "Encountered illegal TEEG_EVENT type %d" % teeg_type )
我不太清楚你所有问题的答案,但对于那些不需要通过值查找的枚举(也就是只是用来避免使用神秘数字),我喜欢用一个小类。普通的字典也是一个不错的选择。如果你需要通过值来查找,可能就需要用其他的数据结构了。
class TeegType(object):
TEEG_EVENT_TAB1 = 1
TEEG_EVENT_TAB2 = 2
print TeegType.TEEG_EVENT_TAB1