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

如何实现仅在写缓冲区满时触发写入的fwrite类似函数?

问题

我想要实现一个类似fwrite的函数,但当前编写的mini_fwrite未能满足需求。我希望仅当写缓冲区(buffer_write)被填满时才触发write系统调用,现有代码如下:

相关结构体定义

typedef struct MYFILE {
    int fd;
    void *buffer_read;
    void *buffer_write;
    int ind_read;
    int ind_write;
} MYFILE;

mini_fopen函数

int IOBUFFER_SIZE = 2048;

MYFILE *mini_fopen(char *file, char mode)
{
    if (file == NULL)
        return NULL;

    MYFILE *fo;

    fo = (MYFILE *)mini_calloc(sizeof(MYFILE), 1);

    if (mode == 'r') fo->fd = open(file, O_RDONLY);
    else if (mode == 'w') fo->fd = open(file, O_WRONLY);
    else if (mode == 'b') fo->fd = open(file, O_RDWR);
    else if (mode == 'a') fo->fd = open(file, O_APPEND);
    else mini_perror("Error\n");

    if (fo->fd == -1) return NULL;
    fo->ind_read = -1;
    fo->ind_write = -1;
    fo->buffer_read = NULL;
    fo->buffer_write = NULL;

    return fo;
}

当前mini_fwrite函数

int mini_fwrite(void *buffer, int size_element, int number_element, MYFILE *file) {
    if (file->ind_write == -1) {
        file->buffer_write = mini_calloc(IOBUFFER_SIZE, 1);
        file->ind_write = 0;
    }

    int i = 0;
    while (i < size_element * number_element) {
        ((char *)file->buffer_write)[file->ind_write] = ((char *)buffer)[i];
        file->ind_write++;
        i++;
        
        if (file->ind_write == IOBUFFER_SIZE) {
            if (write(file->fd, file->buffer_write, file->ind_write) == -1) {
                mini_perror("Error");
            }
            file->ind_write = 0;
        }
    }
    return i;
}
修改方案

原代码存在效率低下(逐字节拷贝)、返回值语义不符、错误处理不严谨的问题。以下是针对性修改:

  • 批量填充缓冲区:计算缓冲区剩余空间,一次性拷贝最大可行字节数,减少循环开销
  • 修正返回值:返回成功写入的元素个数(而非字节数),与标准fwrite行为对齐
  • 增强错误处理:write调用失败时立即返回错误状态,避免无效操作
  • 完善边界逻辑:处理写入数据量远大于缓冲区的场景,确保填满即写

修改后的mini_fwrite代码

int mini_fwrite(void *buffer, int size_element, int number_element, MYFILE *file) {
    // 参数合法性校验
    if (!file || !buffer || size_element <= 0 || number_element <= 0) {
        return 0;
    }

    const int total_bytes = size_element * number_element;
    int bytes_processed = 0;
    char *src_ptr = (char *)buffer;

    // 初始化写缓冲区(未初始化时)
    if (file->ind_write == -1) {
        file->buffer_write = mini_calloc(IOBUFFER_SIZE, 1);
        if (!file->buffer_write) {
            mini_perror("Failed to allocate write buffer");
            return -1;
        }
        file->ind_write = 0;
    }

    char *buf_ptr = (char *)file->buffer_write;

    while (bytes_processed < total_bytes) {
        // 计算缓冲区剩余可写空间
        int buf_remaining = IOBUFFER_SIZE - file->ind_write;
        // 计算本次拷贝的字节数:取剩余数据和缓冲区空间的较小值
        int copy_size = total_bytes - bytes_processed;
        if (copy_size > buf_remaining) {
            copy_size = buf_remaining;
        }

        // 批量拷贝数据到缓冲区
        memcpy(buf_ptr + file->ind_write, src_ptr + bytes_processed, copy_size);
        file->ind_write += copy_size;
        bytes_processed += copy_size;

        // 缓冲区已满,触发系统调用写入
        if (file->ind_write == IOBUFFER_SIZE) {
            ssize_t write_ret = write(file->fd, buf_ptr, IOBUFFER_SIZE);
            if (write_ret == -1) {
                mini_perror("Write system call failed");
                return -1;
            }
            file->ind_write = 0; // 重置缓冲区索引
        }
    }

    // 返回成功写入的元素个数
    return bytes_processed / size_element;
}

补充优化建议

如果需要在缓冲区未填满时强制写入(比如程序退出前),可实现mini_fflush函数:

int mini_fflush(MYFILE *file) {
    if (!file || file->ind_write <= 0 || !file->buffer_write) {
        return 0;
    }

    ssize_t flush_ret = write(file->fd, file->buffer_write, file->ind_write);
    if (flush_ret == -1) {
        mini_perror("Flush failed");
        return -1;
    }
    file->ind_write = 0;
    return 0;
}

另外,原mini_fopen中O_APPEND模式的打开参数建议修正为O_WRONLY | O_APPEND | O_CREAT,否则文件不存在时无法创建,会导致打开失败。

内容的提问来源于stack exchange,提问作者Bayto Malin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 05:35:32