在Python中实现C的枚举和联合体

4 投票
4 回答
6772 浏览
提问于 2025-04-16 04:42

我正在研究一些C语言的代码,想把它移植到Python中。这段代码是用来读取一种专有的二进制数据文件格式。到目前为止,这个过程还算简单,主要是用到结构体(struct),我一直在用struct库从文件中提取特定的数据类型。不过,我遇到了一段代码,搞得我有点迷茫,不知道怎么在Python中实现。特别是,我不太清楚enumunion该怎么处理。

#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 个回答

0

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 )
2

我不太清楚你所有问题的答案,但对于那些不需要通过值查找的枚举(也就是只是用来避免使用神秘数字),我喜欢用一个小类。普通的字典也是一个不错的选择。如果你需要通过值来查找,可能就需要用其他的数据结构了。

class TeegType(object):
    TEEG_EVENT_TAB1 = 1
    TEEG_EVENT_TAB2 = 2

print TeegType.TEEG_EVENT_TAB1
8

枚举(Enums):这个语言里没有枚举类型。虽然有很多方法可以实现类似的功能,但没有一种是特别流行的。最简单(在这个情况下也足够用)的方法是

TEEG_EVENT_TAB1 = 1
TEEG_EVENT_TAB2 = 2

联合体(Unions):ctypes库里有联合体的概念。

fieldname : n这种写法叫做位域,意思是“这个字段占用n位”。同样,ctypes也有位域的用法。

撰写回答