C语言中fgets读取文件行后输出末尾出现乱码的原因及解决方法
为什么用fgets读取文件会输出乱码?
嘿,我帮你定位到问题所在了——乱码的根源是translate函数在处理带换行符的行时,会越界访问栈上的随机内存,具体细节如下:
问题本质
当你用fgets读取文件行时,它会把换行符\n和字符串结束符\0一起写入buf数组。但你的translate函数里,允许保留的字符列表里既不包含\n,也没考虑\0的边界问题:
- 当处理到
\n时,代码会进入else分支的while循环,目的是跳过所有非允许字符。但这个循环的条件里没有检查s[i] != '\0',导致当i指向\0时,循环还会继续执行,i不断往栈内存的深处走(超出buf的范围),去访问那些随机的垃圾数据。 - 这些栈里的随机字节中,万一碰到了属于允许列表的字符(比如字母、数字或者标点),循环就会停下来。之后的代码会把这些栈里的垃圾字符也计入结果的长度,最后设置的
\0结束符位置错误,输出时就带上了这些乱码。
而直接处理字符串常量时没问题,是因为那些字符串末尾只有\0,没有\n,translate的循环会在碰到\0时直接终止,不会触发越界的循环。
修复方法
我们需要做两处调整(第二处可选,看你需求):
1. 给translate函数加上边界检查
在translate的while循环条件里,加上s[i] != '\0',确保不会越界访问内存:
char *translate(char *s) { int count = 0; for (int i = 0; s[i] != '\0'; i++) { if ( (s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= '0' && s[i] <= '9') || s[i] == '.' || s[i] == ',' || s[i] == '!' || s[i] == '?' || s[i] == ' ') { s[count++] = s[i]; } else { // 新增s[i] != '\0',防止越界读取栈垃圾 while (s[i] != '\0' && (s[i] < 'a' || s[i] > 'z') && (s[i] < 'A' || s[i] > 'Z') && (s[i] < '0' || s[i] > '9') && s[i] != '.' && s[i] != ',' && s[i] != '!' && s[i] != '?' && s[i] != ' ') { i++; } i--; } } s[count] = '\0'; return s; }
2. 可选:提前去掉fgets的换行符
因为你的unspace函数会把\n转换成空格,如果你不想输出结果末尾多一个空格,可以在处理前先把换行符删掉,顺便加一些健壮性判断:
int main() { FILE *fp = fopen("data.txt", "r"); if (!fp) { // 增加文件打开失败的判断 perror("Failed to open file"); return EXIT_FAILURE; } char buf[BUFLEN] = {0}; while (fgets(buf, BUFLEN, fp)) { // 去掉换行符(如果存在) size_t len = strlen(buf); if (len > 0 && buf[len-1] == '\n') { buf[len-1] = '\0'; } puts(unspace(toUpper(translate(buf)))); } fclose(fp); // 记得关闭文件 }
修复后的效果
调整完再运行程序,输出就完全正常了:
$ ./a.out AHOJ, JAK SE MAS? MAM SE DOBRE! DNES JE 33 STUPNU.
内容的提问来源于stack exchange,提问作者milanHrabos
相关产品推荐
相关产品推荐

