You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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可以实现自适应缓冲区,既避免内存浪费,也能处理超长行或大文件:

  1. 初始分配一个较小的缓冲区(如1024字节),记录缓冲区总大小和已使用的字节数。
  2. 每次从文件读取数据到缓冲区的剩余空间,若剩余空间不足,将缓冲区容量翻倍(用realloc扩容),避免频繁调用realloc影响性能。
  3. 逐块处理读取到的数据,统计字符、单词、行,无需等待整个文件读完。
  4. 操作完成后务必释放动态分配的内存,避免内存泄漏。

示例代码:

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:11:07