Python3.8用ctypes调用C库fread返回0的原因及二进制读取方法咨询
问题根本原因及解决方法
根本原因
- 字符串类型不兼容:Python3中字符串默认是Unicode类型(
str),而C标准库的fopen函数需要接收字节类型的const char*参数。你代码里传的'rb'是Python3的str,直接传给fopen会导致参数类型错误,实际文件打开失败(虽代码判断了fp is None,但因ctypes类型匹配问题,fp未被正确识别为NULL,后续fread自然读不到内容)。Python2中str本身就是字节串,所以无此问题。 - argtypes定义错误:
fopen的两个参数都是const char*,对应ctypes类型应为ctypes.c_char_p,而非ctypes.c_void_p。错误的argtypes会让ctypes无法正确转换Python对象到C类型,进一步加剧参数传递错误。
修正后的代码
import ctypes import os real_size = 282624 buffer = (ctypes.c_ubyte * real_size)() length = real_size file_path = "你的文件路径" # 替换为实际文件路径 libc = ctypes.CDLL(ctypes.util.find_library("c")) # 正确定义fopen的参数与返回值类型 libc.fopen.restype = ctypes.c_void_p libc.fopen.argtypes = (ctypes.c_char_p, ctypes.c_char_p) # 将文件路径和模式转为bytes类型传入 fp = libc.fopen(file_path.encode('utf-8'), b'rb') if fp is None: raise OSError(f'Cannot open file {file_path}') libc.fclose.argtypes = (ctypes.c_void_p,) # 定义fseek的参数与返回值类型 libc.fseek.restype = ctypes.c_int libc.fseek.argtypes = (ctypes.c_void_p, ctypes.c_long, ctypes.c_int) if libc.fseek(fp, 0, os.SEEK_SET) != 0: libc.fclose(fp) raise IOError(f"fseek to 0 failed") # 定义fread的参数与返回值类型 libc.fread.argtypes = (ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t, ctypes.c_void_p) libc.fread.restype = ctypes.c_size_t bytes_read = libc.fread(ctypes.addressof(buffer), 1, length, fp) print(f"Bytes read: {bytes_read}") # 操作完成后关闭文件 libc.fclose(fp) # 可选:将ctypes数组转为Python的bytes对象 data = bytes(buffer[:bytes_read])
使用fread读取二进制文件的正确步骤
- 严格匹配参数类型:传给C函数的参数必须和C函数期望类型完全一致,Python3中字符串参数要转为
bytes,同时正确设置argtypes和restype,这是ctypes调用C函数的核心要求。 - 指定二进制打开模式:用
b'rb'作为打开模式,明确以二进制只读方式打开,避免文本模式下的换行符转换、编码处理等干扰。 - 检查每一步返回值:打开文件后检查
fp是否为NULL,fseek后检查返回值是否为0,确保每一步操作成功,避免后续操作在错误状态下执行。 - 读取后转换处理:读取到ctypes数组的内容,可通过
bytes(buffer[:bytes_read])转为Python的bytes对象,方便后续业务处理。 - 及时释放资源:操作完成后调用
fclose关闭文件,避免资源泄漏。
内容的提问来源于stack exchange,提问作者taotao
相关产品推荐
相关产品推荐

