C语言文件行读取函数异常排查:乱码、空文件行数统计错误
问题排查与修复
以下是你的代码中存在的核心问题及对应的修复方案:
1. 文件指针位置未重置
调用count_number_of_lines后,文件指针会移动到文件末尾(EOF)。后续调用getLineFromFile时,fgets无法读取任何内容,导致字符串内存未初始化,输出乱码。
修复:在统计行数后调用rewind(fp)将文件指针重置到文件开头。
2. 空行统计逻辑错误
原count_number_of_lines函数会将仅包含换行符的空行计入统计(因为fgets会读取换行符,strlen(line)结果为1),不符合"统计非空行"的需求;虽空文件返回0是正确的,但非空文件中的空行统计有误。
修复:读取每行后先去掉换行符,再检查是否包含非空白字符。
3. getLineFromFile函数边界处理不当
- 当指定行号超过文件实际行数时,
fgets会返回NULL,此时直接修改字符串会导致未定义行为。 - 若行末尾无换行符(如文件最后一行),
str[strlen(str)-1] = '\0'会错误修改有效字符。
修复:跟踪最后一次成功读取的行,仅当行末尾存在换行符时才移除它;若未读取到任何行,释放内存并返回NULL。
4. 内存泄漏
main和compiled函数中初始malloc的指针被getLineFromFile的返回值覆盖,导致内存泄漏。getLineFromFile返回的字符串未被释放,多次调用会累积内存泄漏。
修复:不再提前malloc指针,每次使用getLineFromFile返回的字符串后立即释放。
修复后的完整代码
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <ctype.h> char* getLineFromFile(FILE *fp, int lineNumber) // 若行号超出总行数,返回最后一行 { char *str = malloc(sizeof(char) * 80); if (!str) return NULL; // 处理内存分配失败 int i; int has_read = 0; for (i = 1 ; i <= lineNumber; i++) { if (fgets(str, 80, fp) == NULL) { break; // 到达文件末尾,停止读取 } has_read = 1; } if (has_read) { // 仅当行末尾存在换行符时移除 size_t len = strlen(str); if (len > 0 && str[len-1] == '\n') { str[len-1] = '\0'; } return str; } else { // 未读取到任何内容,释放内存并返回NULL free(str); return NULL; } } /// 统计文件中的非空行(不含仅空白字符的行) int count_number_of_lines(FILE *fp) { int count = 0; char line[256]; while (fgets(line, sizeof(line), fp) != NULL) { size_t len = strlen(line); // 移除换行符 if (len > 0 && line[len-1] == '\n') { line[--len] = '\0'; } // 检查是否包含非空白字符 int is_empty = 1; for (size_t j = 0; j < len; j++) { if (!isspace((unsigned char)line[j])) { is_empty = 0; break; } } if (!is_empty) { count++; } } return count; } int compiled(FILE* fp){ int nb_lines = count_number_of_lines(fp); rewind(fp); // 重置文件指针到开头 printf("number of lines : %d \n", nb_lines); for (int j=1; j<=nb_lines; j++){ char *line = getLineFromFile(fp, j); if (line) { printf("%s \n", line); free(line); // 释放内存 } } return 0; } int main() { FILE *fichier = fopen("CodeSource.txt", "r"); if (!fichier) { perror("无法打开文件"); return 1; } int nb_lines = count_number_of_lines(fichier); rewind(fichier); // 重置文件指针到开头 for (int j=1; j<=nb_lines; j++){ char *str = getLineFromFile(fichier, j); if (str) { printf("%s \n", str); free(str); // 释放内存 } } fclose(fichier); // 关闭文件 return 0; }
额外说明
- 代码中增加了
fclose(fichier)以确保文件资源被正确释放。 - 添加了内存分配失败的处理逻辑,避免程序崩溃。
- 若需要支持超过79字符的行,需修改
getLineFromFile的内存分配逻辑(如动态扩容),当前代码假设每行不超过79字符。
内容的提问来源于stack exchange,提问作者Kiko
相关产品推荐
相关产品推荐

