C语言文件读取之fread乱码的处理

上篇文章关于fread遗留了一个问题,就是在使用fread读取文件,打印时汉字如果只读取了部分,此时打印出来的会是乱码,这篇文章用来解决该遗留问题:


直接上代码,次代码只考虑了linux环境,请注意

#include 
#include 
#include 

#define CHUNK_SIZE 256

// 函数声明
int myFread(const char *filename);

int main() {
    const char *filename = "test.txt";
    return myFread(filename) ? 1 : 0;
}

int myFread(const char *filename) {
    // 打开文件
    FILE *file = fopen(filename, "rb");
    if (!file) {
        perror("fopen error");
        return -1;
    }

    // 处理UTF-8 BOM
    unsigned char bom[3];
    size_t header_read = fread(bom, 1, 3, file);
    if (header_read == 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) {
        // 跳过BOM
    } else {
        fseek(file, 0, SEEK_SET);
    }

    // 分块读取缓冲区
    char buffer[CHUNK_SIZE + 4] = {0}; // +4防止UTF-8字符截断
    size_t leftover = 0; // 记录未处理字节数

    while (1) {
        // 读取数据(留出空间处理残留字节)
        size_t read_size = fread(buffer + leftover, 1, CHUNK_SIZE, file);
        if (read_size == 0) {
            // 处理最后剩余的字节
            if (leftover > 0) fwrite(buffer, 1, leftover, stdout);
            break;
        }

        // 计算有效数据长度
        size_t total_size = leftover + read_size;
        
        /* UTF-8截断保护逻辑:
         * 1. 从末尾向前找到第一个UTF-8起始字节
         * 2. 有效数据截止到该位置
         * 3. 残留字节移动到buffer开头
         */
        int safe_pos = total_size - 1;
        while (safe_pos >= 0 && (buffer[safe_pos] & 0xC0) == 0x80) {
            safe_pos--; // 跳过UTF-8后续字节(10xxxxxx)
        }

        // 计算有效输出长度
        size_t valid_len = (safe_pos >= 0) ? (safe_pos + 1) : 0;
        
        // 输出有效数据
        if (valid_len > 0) {
            fwrite(buffer, 1, valid_len, stdout);
        }

        // 处理残留字节(最多3个)
        leftover = total_size - valid_len;
        if (leftover > 0) {
            memmove(buffer, buffer + valid_len, leftover);
        }
    }

    fclose(file);
    return 0;
}

  1. 内存优化
  • 使用固定大小(256+4字节)的循环缓冲区
  • 每次读取CHUNK_SIZE字节(避免大内存消耗)
  • 通过memmove实现残留字节处理(最多保留3字节)
  1. 汉字防乱码设计
  • BOM处理:自动检测并跳过UTF-8 BOM
  • 截断保护:通过逆向查找UTF-8起始字节,确保不分割多字节字符
  • 缓冲区设计:+4字节缓冲区防止字符截断
  1. 代码结构
  • 核心逻辑封装在myFread()函数
  • main函数只负责调用
  • 使用符号常量CHUNK_SIZE控制读取粒度

编译运行

gcc -o reader reader.c && ./reader

性能特点:

  • 内存占用恒定(约260字节)
  • 可安全处理TB级大文件
  • 自动兼容含汉字的UTF-8文本

处理效果对比:

场景

原始代码

优化代码

10GB大文件

内存爆炸

稳定运行

含BOM的文件

正常显示

自动跳过

汉字跨区块的情况

出现乱码

自动拼接

内存占用

O(n)

O(1)

该版本在保持功能完整性的同时,显著提升了内存安全性和大文件处理能力。