使用fgetc统计文件中指定十六进制数的实现及问题代码排查
能否使用fgetc统计文件中特定十六进制数值的出现次数
完全可以。fgetc每次调用会从以二进制模式打开的文件中读取1个字节,刚好匹配单字节十六进制值的统计场景,你给出的示例中查询0xFF返回1的需求完全可以通过该函数实现。
你现有代码运行结果不符合预期,是几个典型的C语言文件操作错误导致的。
现有代码的错误点
- 文件读取循环逻辑错误:
while(!feof(in))是C语言文件操作的常见问题。feof只有在已经触发过文件末尾读取操作后才会返回真值,当读完文件最后一个有效字节后,下一次fgetc调用会返回EOF(值为-1),这个无效值会被你写入数组,同时计数器i会多累加1次。 - 无效值误判:
fgetc返回值是int类型,设计目的就是为了区分0x00~0xFF范围的有效字节和特殊标记EOF。你把返回值直接存入unsigned char类型的数组时,EOF(-1)会被转换为0xFF,这也是你统计0xFF时结果偏大的核心原因——读取到文件末尾产生的假0xFF也被计入了统计。 - 数组越界风险:你定义的存储数组固定长度为1000字节,一旦目标文件大小超过1000字节,写入数组时会发生内存越界,引发程序崩溃或逻辑异常。
- 统计范围错误:统计时你遍历了整个1000长度的数组,而非实际从文件中读取到的有效字节范围。数组初始化时所有元素都是0,如果查询值是
0x00,所有未写入内容的数组元素都会被计入结果,统计值完全错误。 - 缺少异常校验:如果
a.dat不存在、当前用户没有读取权限,fopen会返回NULL,后续直接操作空指针会导致程序直接崩溃。
修正后的实现代码
实际上统计字节出现次数根本不需要把整个文件读入内存存储,边读取边比对计数即可,既不会有内存长度限制,运行效率也更高,修正后的代码如下:
#include <stdio.h> #include <stdlib.h> int main() { FILE *in = NULL; int read_byte; unsigned int target_val; int count = 0; // 二进制只读模式打开文件,校验打开结果 in = fopen("a.dat", "rb"); if (in == NULL) { printf("打开文件a.dat失败,请检查文件是否存在及权限\n"); return EXIT_FAILURE; } // 读取用户输入的待查询十六进制值 printf("请输入要查询的单字节十六进制值(例如输入FF代表0xFF):"); scanf("%x", &target_val); if (target_val > 0xFF) { printf("输入非法,单字节十六进制值范围为00~FF\n"); fclose(in); return EXIT_FAILURE; } // 逐字节读取文件,匹配则计数 while ((read_byte = fgetc(in)) != EOF) { if ((unsigned char)read_byte == target_val) { count++; } } // 输出结果到控制台和b.txt printf("统计结果:%d\n", count); FILE *out = fopen("b.txt", "wb"); if (out != NULL) { fprintf(out, "%d\n", count); fclose(out); } fclose(in); return EXIT_SUCCESS; }
代码逻辑说明
- 移除了固定长度数组,采用逐字节读取即时比对的逻辑,支持任意大小的文件统计,不存在数组越界问题。
- 修正了文件读取循环判断条件,直接通过
fgetc返回值是否为EOF判断是否读到文件末尾,避免多读无效字节。 - 比对时将
fgetc返回的int类型值显式转为unsigned char,保证和0~0xFF范围的目标值匹配逻辑正确,不会把EOF误判为有效字节0xFF。 - 增加了文件打开、输入值合法性校验,避免异常场景下程序崩溃。
- 统计范围仅覆盖实际从文件中读取到的有效字节,不会引入无关初始值的干扰。
用你给出的测试字节序列验证:输入FF时,程序会正确返回统计值1,符合预期。
内容的提问来源于stack exchange,提问作者cat
相关产品推荐
相关产品推荐

