C语言:使用strcmp比较文本文件相邻行时末尾行异常问题
这个问题我太熟了——fgets的换行符陷阱几乎是每个C新手都会踩的坑!咱们一步步拆解:
首先,你看到的额外字符十有八九是换行符('\n'),甚至可能是Windows系统下的回车符('\r')。fgets的特性是:它会把输入中的换行符一起读进缓冲区,直到遇到换行或EOF才停止,而且会自动在末尾加'\0'。
比如你的例子里,倒数第二行"four"后面跟着换行符,所以fgets读出来的是"four\n",strlen算出来是5('f'+'o'+'u'+'r'+'\n');而最后一行如果是Windows格式的结尾,会带有\r\n,读出来就是"four\r\n",长度直接变成6。这时候两个字符串一个是"four\n"一个是"four\r\n",strcmp自然会返回非0,因为底层内容根本不一样。
那怎么解决?核心就是读取每行后,把末尾的换行/回车符去掉,统一格式再比较。
我给你写个实用的处理函数,能同时兼容Unix('\n')和Windows('\r\n')的换行格式:
void trim_trailing_newline(char *str) { if (!str) return; size_t len = strlen(str); // 先去掉换行符 if (len > 0 && str[len - 1] == '\n') { str[--len] = '\0'; // 如果去掉换行后末尾是回车,再去掉回车 if (len > 0 && str[len - 1] == '\r') { str[--len] = '\0'; } } }
然后修改你的读取逻辑,每次用fgets读行后,先调用这个函数处理:
#include <stdio.h> #include <string.h> void trim_trailing_newline(char *str) { if (!str) return; size_t len = strlen(str); if (len > 0 && str[len - 1] == '\n') { str[--len] = '\0'; if (len > 0 && str[len - 1] == '\r') { str[--len] = '\0'; } } } int main() { char prev_line[256], curr_line[256]; // 读取第一行,处理空文件或读取错误的边界情况 if (!fgets(prev_line, sizeof(prev_line), stdin)) { fprintf(stderr, "空文件或读取错误\n"); return 1; } trim_trailing_newline(prev_line); while (fgets(curr_line, sizeof(curr_line), stdin)) { trim_trailing_newline(curr_line); // 现在比较就没问题了 int cmp_result = strcmp(prev_line, curr_line); if (cmp_result == 0) { printf("相邻行相等:%s\n", prev_line); } else { printf("相邻行不等:%s vs %s(strcmp返回值:%d)\n", prev_line, curr_line, cmp_result); } // 把当前行复制到前一行,准备下一次循环 strcpy(prev_line, curr_line); } return 0; }
另外还要注意两个小细节:
- 确保你的缓冲区大小足够容纳最长的行,如果行长度超过缓冲区,fgets会截断,导致读取不完整,这时候也会出现奇怪的比较结果。如果不确定行长度,可以用动态分配的缓冲区(比如用malloc配合realloc)。
- 检查fgets的返回值,如果返回NULL,要么是到了EOF,要么是读取错误,要正确处理这种情况,避免访问无效的内存。
这样修改后,不管每行末尾有没有换行,也不管是Unix还是Windows格式的换行,都能统一成没有换行符的纯内容,strcmp就能正确比较了。
内容的提问来源于stack exchange,提问作者Calahan
相关产品推荐
相关产品推荐

