Python read与C语言fread读取同一二进制文件数据显示不一致原因
同JPG文件Python/C二进制读取结果不一致问题
问题现象
分别使用Python、C语言以二进制只读(rb)模式打开同一份39KB大小的JPG文件读取内容:
- Python侧读取得到文件长度为39038字节,输出的起始二进制序列为
\xff\xd8\xff\xe0,参考代码如下:
aaa = open('C:\simfile\\testphoto.jpg','rb') a = aaa.read() print(len(a)) # 输出39038 print(a)
- C语言侧使用
fread读取同一份文件后打印十六进制内容,输出结果和Python侧存在明显差异,参考代码如下:
#include <stdio.h> #include <string.h> #include<iostream> #include <io.h> #include<fcntl.h> using namespace std; int main() { FILE *fp; fp = fopen("D:\\CbProject\\testphoto2.jpg", "rb"); fseek(fp, 0, SEEK_SET); int mysize = 39038+1; rewind(fp); char buffer[mysize]; fread(buffer, mysize, 1, fp); for(int i = 0;i<mysize;i++) { printf("%2x ", buffer[i] ); } fclose(fp); return(0); }
问题根因
两边实际读取到的文件原始字节完全一致,输出差异是C代码存在写法缺陷导致的,具体问题点:
- 核心问题是有符号char的符号扩展导致打印错误:x86平台下绝大多数C编译器默认将
char类型定义为有符号类型(等价于signed char),取值范围为-128~127。当读取到的字节值≥0x80(十进制128)时,存入char变量会被识别为负数;调用printf通过%x输出时,char类型会先自动提升为int类型,负数提升时会将高位全部填充1做符号扩展,例如原始字节0xff存入signed char为-1,提升为32位int后值为0xffffffff,最终打印出8位f,和Python输出的单字节ff完全不符。 - 存在越界读取无效值的问题:硬编码的读取长度
mysize = 39038+1比文件实际长度多1字节,fread最多仅会读取文件内真实存在的39038字节,数组最后1位是栈上未初始化的随机内存值,打印出的这1字节本身就是无效垃圾数据。 - 格式化输出符使用不当:
%2x仅保证输出宽度为2、右对齐,遇到值小于0x10的字节(如0x01、0x0a)时不会补前导0,输出为单个数字,和Python侧的两位十六进制展示格式不一致。 - 额外冗余:代码中先后调用
fseek(fp, 0, SEEK_SET)和rewind(fp)都是将文件指针移到开头,属于重复操作,不影响结果但无必要。
修复方案
将存储字节的数组改为unsigned char类型避免符号扩展,动态获取文件真实长度替代硬编码,同时修正格式化输出符,修复后代码如下:
#include <stdio.h> int main() { FILE *fp = fopen("D:\\CbProject\\testphoto2.jpg", "rb"); if (!fp) { perror("打开文件失败"); return 1; } // 动态获取文件真实长度 fseek(fp, 0, SEEK_END); int file_size = ftell(fp); rewind(fp); // 用无符号字符类型存储字节,从根源避免符号扩展问题 unsigned char buffer[file_size]; // 正确传参fread:单元素大小1字节,共读取file_size个元素 size_t read_num = fread(buffer, 1, file_size, fp); if (read_num != file_size) { perror("读取文件失败"); fclose(fp); return 1; } for(int i = 0; i < file_size; i++) { // 用%02x固定输出两位十六进制,不足补前导0 printf("%02x ", buffer[i]); } fclose(fp); return 0; }
修复后重新运行,C侧打印的十六进制内容会和Python侧输出完全一致。
内容的提问来源于stack exchange,提问作者kireirain
相关产品推荐
相关产品推荐

