C语言fread方法读取文件二进制数据显示异常原因排查
问题场景
- Python读取验证:首先使用Python的read方法以二进制模式读取路径
D:\CbProject\testphoto2.jpg文件,打印二进制byte类型的十六进制数据,实现代码如下:
aaa = open('D:\\CbProject\\testphoto2.jpg','rb') a = aaa.read() print(a) # 十六进制显示 二进制byte类型数据 print(len(a)) # 39038
Python读取结果正常,返回数据类型为bytes,文件总长度为39038字节,文件起始二进制序列为\xff \xd8 \xff \xe0 ......,符合JPEG文件头特征。
- C语言读取测试:随后使用C语言的fread方法读取同一文件并打印二进制形式,实现代码如下:
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MYSIZE 39038 int main(void) { FILE *fp; if((fp = fopen("D:\\CbProject\\testphoto2.jpg", "rb")) == NULL) { fprintf(stderr, "can't open file\n"); exit(1); } char buffer[MYSIZE]; if(fread(buffer, sizeof(buffer), 1, fp) != 1) { fprintf(stderr, "read failed"); exit(1); } for (int i = 0; i < sizeof(buffer); i++) { printf("%2x ", buffer[i]); } fclose(fp); return(0); }
异常现象
运行上述C语言代码后,打印结果与Python读取的正确结果存在明显差异:C语言中char类型仅可存储1字节数据,单字节最大取值为0xff,但打印结果中大量数据出现多余前导f,超出单字节取值范围;同时使用%2x格式符打印时,出现单个1、2、3等不符合预期的输出。
异常原因
两个异常分别由C语言的固有特性导致:
- 符号扩展问题:C语言标准未强制规定
char类型默认是有符号还是无符号,绝大多数x86、x64平台的编译器默认将char处理为有符号类型,取值范围为-128~127。当读取到的字节值大于等于0x80时,对应有符号char的值为负数;而printf是可变参数函数,传入的char类型参数会自动做整型提升为int类型,负数提升时会触发符号扩展,高位全部补1,比如字节值0xff对应有符号char值为-1,提升为int后值为0xffffffff,用十六进制打印就会出现多余的前导f。 - 格式符占位规则问题:
%2x仅指定输出最小宽度为2个字符,当输出值小于0x10(即十六进制仅占1位)时,默认用空格填充宽度,不会自动补前导0,因此会出现单个数字的输出。
解决方案
针对两个问题对应修复即可:
- 将存储二进制数据的缓冲区类型从
char改为unsigned char,无符号char取值范围为0~255,整型提升时高位统一补0,不会触发符号扩展导致的多余f问题。 - 将打印格式符从
%2x改为%02x,格式符中的0标识指定宽度不足时用前导0填充,保证每个字节固定输出2位十六进制字符。
修正后的核心代码片段:
// 缓冲区类型改为unsigned char unsigned char buffer[MYSIZE]; if(fread(buffer, sizeof(buffer), 1, fp) != 1) { fprintf(stderr, "read failed"); exit(1); } for (int i = 0; i < sizeof(buffer); i++) { // 格式符改为%02x printf("%02x ", buffer[i]); }
内容的提问来源于stack exchange,提问作者kireirain
相关产品推荐
相关产品推荐

