C语言读取含换行输入及实现wc工具的问题与内存优化求助
解决fgets仅读取单行的问题
fgets的特性是读取到换行符或缓冲区满时停止,要处理整个文件,必须将其放入循环,持续读取直到文件末尾。
核心实现逻辑:用while(fgets(buf, buf_len, fp) != NULL)作为循环条件,每次读取后立即处理当前缓冲区的内容,同时保持统计状态(比如是否处于单词中、是否有未计数的行)在循环间不重置,避免遗漏跨缓冲区的统计项。
关键注意点:
- 行计数:每次读取到换行符时行数+1;循环结束后,若缓冲区有内容但未读到换行符,说明最后一行无换行,需额外+1。
- 单词计数:维护一个布尔变量(如
in_word),标记当前是否处于单词内。遍历缓冲区字符时,若当前字符是非空白且in_word为假,则单词数+1并置in_word为真;若当前字符是空白则置in_word为假。这个变量必须在循环外初始化,不能每次循环重置。 - 字符计数:直接累加每次
fgets读取的有效字符数(strlen(buf),注意fgets会包含换行符,符合wc的统计逻辑)。
按需分配内存的实现
固定内存分配不够灵活,用malloc+realloc可以实现自适应缓冲区,既避免内存浪费,也能处理超长行或大文件:
- 初始分配一个较小的缓冲区(如1024字节),记录缓冲区总大小和已使用的字节数。
- 每次从文件读取数据到缓冲区的剩余空间,若剩余空间不足,将缓冲区容量翻倍(用
realloc扩容),避免频繁调用realloc影响性能。 - 逐块处理读取到的数据,统计字符、单词、行,无需等待整个文件读完。
- 操作完成后务必释放动态分配的内存,避免内存泄漏。
示例代码:
#include <stdio.h> #include <stdlib.h> #include <ctype.h> int main() { FILE *fp = fopen("lorem.txt", "r"); if (!fp) { perror("fopen"); return 1; } size_t buf_capacity = 1024; char *buf = malloc(buf_capacity); if (!buf) { perror("malloc"); fclose(fp); return 1; } size_t chars = 0, words = 0, lines = 0; int in_word = 0; size_t total_read = 0; ssize_t bytes_read; while ((bytes_read = fread(buf + total_read, 1, buf_capacity - total_read, fp)) > 0) { total_read += bytes_read; chars += bytes_read; // 遍历当前读取的内容,统计单词和行 for (size_t i = total_read - bytes_read; i < total_read; i++) { if (buf[i] == '\n') { lines++; } if (isspace(buf[i])) { in_word = 0; } else if (!in_word) { words++; in_word = 1; } } // 缓冲区已满,扩容 if (total_read == buf_capacity) { buf_capacity *= 2; char *new_buf = realloc(buf, buf_capacity); if (!new_buf) { perror("realloc"); free(buf); fclose(fp); return 1; } buf = new_buf; } } // 处理最后一行无换行的情况 if (chars > 0 && buf[total_read - 1] != '\n') { lines++; } printf("%zu %zu %zu\n", lines, words, chars); free(buf); fclose(fp); return 0; }
说明:这里用fread替代fgets更适合动态缓冲区的逐块读取,避免fgets因换行中断读取的问题,同时更高效地处理大文件。如果坚持用fgets,也可以用类似的动态扩容逻辑,当fgets读取的长度等于缓冲区大小且最后一个字符不是换行时,扩容缓冲区继续读取当前行。
内容的提问来源于stack exchange,提问作者Omaro_IB
相关产品推荐
相关产品推荐

