使用fgets读取文件首行时始终出现3个无效字符的技术问题
解决读取文本文件首行出现乱码字符的问题
兄弟,你这遇到的是典型的**UTF-8字节顺序标记(BOM)**的坑!那些看起来诡异的(调试控制台)或者∩╗┐(终端),本质是文件开头的三个特殊字节0xEF 0xBB 0xBF——也就是UTF-8的BOM——被你的C代码当成普通ASCII字符解析后显示出来的乱码。
为啥会出现这问题?
- 有些编辑器(比如Windows自带的记事本)在保存UTF-8格式文件时,会自动在文件最开头加上这三个字节作为标识,但UTF-8本身其实不需要BOM
- 你用
fgets读取时,C标准库的默认文本模式不会自动识别并跳过这个BOM,所以直接把它当成了文本内容的一部分读进来 - 删掉首行后,BOM还是在文件最开头,所以新的首行自然也带着它;而加了空行后,BOM被包含在空行里,后续的有效内容就不受影响了
给你几个解决办法:
1. 直接去掉文件里的BOM
用支持编码设置的编辑器重新保存文件就行:
- VS Code:点击右下角的编码标识(比如显示“UTF-8”),选择「通过编码保存」,然后选「UTF-8」(别选带BOM的选项)
- Notepad++:顶部菜单「编码」→「转为UTF-8无BOM格式」
2. 在代码里自动跳过BOM
如果没法修改文件,或者需要兼容带BOM的文件,可以在读取第一行时检测并跳过这三个字节:
#include <stdio.h> #include <string.h> #define LINELENGTH 256 int main() { FILE *stream = fopen("your_input_file.txt", "r"); if (!stream) { perror("Failed to open file"); return 1; } char line[LINELENGTH]; int is_first_line = 1; while (fgets(line, LINELENGTH, stream) != NULL) { if (is_first_line) { // 检查开头是否是UTF-8 BOM if (strlen(line) >= 3 && (unsigned char)line[0] == 0xEF && (unsigned char)line[1] == 0xBB && (unsigned char)line[2] == 0xBF) { // 把BOM后的内容移到开头,去掉BOM memmove(line, line + 3, strlen(line) - 2); } is_first_line = 0; } // 这里处理你的业务逻辑,比如打印或者解析 printf("%s", line); } fclose(stream); return 0; }
这里用memmove而不是strcpy是因为源和目标内存区域有重叠,memmove能安全处理这种情况。
3. 二进制模式读取(不推荐,除非必要)
如果需要更底层的控制,可以用二进制模式打开文件(fopen的第二个参数用"rb"),手动检测BOM后再处理文本,但这种方式需要自己处理Windows下的\r\n换行符转\n,一般没必要这么折腾。
后续预防
- 以后保存文本文件时,记得选无BOM的UTF-8格式
- 如果是自己写程序生成文件,确保输出时不要添加UTF-8 BOM
内容的提问来源于stack exchange,提问作者Carpo Crates
相关产品推荐
相关产品推荐

