读写操作过程和性能

C/C++读写操作的过程和性能

  • 在C/C++中,有多种处理读写的函数,主要分为低级别I/O函数和高级别I/O函数。每种函数都有其特定的优缺点和适用场景。

读写函数

低级别I/O函数

read()write()
  • 头文件<unistd.h>
  • 级别:低级别I/O(系统调用)
  • 优点
    • 直接操作文件描述符,效率高。
    • 适用于处理二进制文件和性能要求较高的场景。
    • 提供更精细的控制(如非阻塞I/O、异步I/O)。
  • 缺点
    • 不提供缓冲机制,需要手动管理缓冲区。
    • 接口相对复杂,不适合处理文本文件的高级操作。
示例:
#include <unistd.h>
#include <fcntl.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    char buffer[128];
    ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
    close(fd);
    return 0;
}

高级别I/O函数(C标准库)

fgets()fputs()
  • 头文件<stdio.h>
  • 级别:高级别I/O(标准库函数)
  • 优点
    • 使用缓冲机制,操作更方便。
    • 适用于文本文件的行级操作。
  • 缺点
    • 性能相对低于低级别I/O函数,不适合高性能需求的场景。
示例:
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    char buffer[128];
    fgets(buffer, sizeof(buffer), file);
    fclose(file);
    return 0;
}
fread()fwrite()
  • 头文件<stdio.h>
  • 级别:高级别I/O(标准库函数)
  • 优点
    • 使用缓冲机制,操作方便。
    • 适用于处理二进制文件和大块数据读写。
  • 缺点
    • 性能相对低于低级别I/O函数,不适合高性能需求的场景。
示例:
#include <stdio.h>

int main() {
    FILE *file = fopen("example.bin", "rb");
    char buffer[128];
    fread(buffer, sizeof(char), sizeof(buffer), file);
    fclose(file);
    return 0;
}
fprintf()fscanf()
  • 头文件<stdio.h>
  • 级别:高级别I/O(标准库函数)
  • 优点
    • 使用缓冲机制,操作方便。
    • 适用于格式化的文本文件读写。
  • 缺点
    • 性能相对低于低级别I/O函数,不适合高性能需求的场景。
示例:
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w");
    fprintf(file, "Hello, World!\n");
    fclose(file);
    return 0;
}

高级别I/O函数(C++标准库)

ifstreamofstream
  • 头文件<fstream>
  • 级别:高级别I/O(C++标准库)
  • 优点
    • 提供面向对象的接口,使用方便。
    • 使用缓冲机制,适合处理文本文件和二进制文件。
    • 继承了C++的类型安全和异常处理机制。
  • 缺点
    • 性能相对低于低级别I/O函数,不适合高性能需求的场景。
示例:
#include <fstream>

int main() {
    std::ifstream file("example.txt");
    std::string line;
    std::getline(file, line);
    file.close();
    return 0;
}
iostream
  • 头文件<iostream>
  • 级别:高级别I/O(C++标准库)
  • 优点
    • 提供面向对象的接口,使用方便。
    • 使用缓冲机制,适合处理文本文件。
    • 继承了C++的类型安全和异常处理机制。
  • 缺点
    • 性能相对低于低级别I/O函数,不适合高性能需求的场景。
示例:
#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

读写函数优缺点总结

函数头文件优点缺点适用场景
read()<unistd.h>高效,直接系统调用,适合二进制文件和高性能需求无缓冲机制,接口复杂二进制文件,高性能I/O
write()<unistd.h>高效,直接系统调用,适合二进制文件和高性能需求无缓冲机制,接口复杂二进制文件,高性能I/O
fgets()<stdio.h>使用缓冲机制,适合文本文件行级操作性能相对较低文本文件,行级操作
fputs()<stdio.h>使用缓冲机制,适合文本文件行级操作性能相对较低文本文件,行级操作
fread()<stdio.h>使用缓冲机制,适合处理二进制文件和大块数据读写性能相对较低二进制文件,大块数据读写
fwrite()<stdio.h>使用缓冲机制,适合处理二进制文件和大块数据读写性能相对较低二进制文件,大块数据读写
fprintf()<stdio.h>使用缓冲机制,适合格式化文本文件读写性能相对较低格式化文本文件
fscanf()<stdio.h>使用缓冲机制,适合格式化文本文件读写性能相对较低格式化文本文件
ifstream<fstream>面向对象接口,使用方便,适合处理文本和二进制文件性能相对较低文本和二进制文件
ofstream<fstream>面向对象接口,使用方便,适合处理文本和二进制文件性能相对较低文本和二进制文件
iostream<iostream>面向对象接口,使用方便,适合处理文本文件性能相对较低文本文件
  • <stdio.h>头文件里的BUFSIZ宏定义了默认的缓冲区大小

fgets()过程示例

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    char buffer[128];
    fgets(buffer, sizeof(buffer), file);
    fclose(file);
    return 0;
}

在使用 fgets() 函数读取文件时,文件的实际大小(例如100MB)与缓冲机制的工作原理密切相关。尽管 fgets() 只读取文件中的一行,但在幕后,标准I/O库会利用缓冲区来优化文件的读取性能。以下是详细说明 fgets() 使用缓冲机制的过程:

步骤及缓冲机制

  1. 打开文件fopen("example.txt", "r") 打开文件 example.txt 并返回文件指针 file

    FILE *file = fopen("example.txt", "r");
    
  2. 初始化缓冲区:标准I/O库为文件流分配一个缓冲区。默认缓冲区大小通常为 4KB 或 8KB,由具体的系统和库实现决定。这个缓冲区在内部维护,不需要程序员显式分配。

  3. 调用 fgets() 读取数据

    • 调用 fgets(buffer, sizeof(buffer), file)
    • fgets() 首先检查文件流缓冲区中是否有足够的数据。如果缓冲区为空或数据不足,则进行一次实际的读操作,将数据从磁盘读取到缓冲区中。
    char buffer[128];
    fgets(buffer, sizeof(buffer), file);
    
  4. 填充缓冲区

    • 假设缓冲区大小为 4KB,标准I/O库会尝试读取最多 4KB 的数据到缓冲区。
    • 如果缓冲区数据不够或为空,标准I/O库会调用低级系统调用(如 read())从磁盘读取数据到缓冲区中。
    • 读取的数据会存放在缓冲区中以便后续读取。
  5. 读取一行数据

    • fgets() 从缓冲区中读取一行数据并存储到用户提供的 buffer 中。
    • fgets() 会读取最多 sizeof(buffer) - 1 个字符,或直到遇到换行符 \n 或文件结束符 EOF。
    • 此外,fgets() 确保读取的字符串以空字符 \0 结尾。
  6. 返回数据

    • 一旦读取到一行数据,fgets() 将数据存储在 buffer 中并返回指向该缓冲区的指针。
    fgets(buffer, sizeof(buffer), file);
    
  7. 关闭文件:完成读操作后,调用 fclose(file) 关闭文件,释放文件流和缓冲区。

    fclose(file);
    

缓冲机制的优势

  • 减少系统调用:缓冲区机制可以减少系统调用的次数。每次 fgets() 操作不会都直接导致系统调用,而是尽可能从缓冲区读取数据,只有当缓冲区数据不足时,才进行实际的磁盘读操作。
  • 提高性能:通过批量读取数据到缓冲区,减少了频繁的磁盘I/O操作,提高了文件读取的效率。
  • 方便使用:用户只需调用高级函数(如 fgets()),不需要关心底层的缓冲区管理和系统调用。

示例解释

假设 example.txt 文件大小为 100MB:

  1. 第一次调用 fgets():文件指针位置在文件开头,缓冲区为空。
  2. 填充缓冲区:标准I/O库从文件读取 4KB 数据到缓冲区。
  3. 读取数据fgets() 从缓冲区读取一行数据,假设该行数据小于 128 字节。
  4. 后续调用 fgets():如果继续调用 fgets(),将从缓冲区读取剩余的数据,直到缓冲区数据不足,再次从文件读取下一批数据。

这种缓冲机制确保了即使文件非常大(如 100MB),每次调用 fgets() 时也只是处理较小的缓冲区内容,从而避免了直接处理大量数据的开销。

fputs()过程示例

fputs() 函数用于将一个字符串写入到文件中,与 fgets() 类似,fputs() 也利用了标准I/O库的缓冲机制来优化文件写入操作。以下是 fputs() 使用缓冲机制的详细过程:

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w");
    if (file) {
        const char *text = "This is a test string to demonstrate fputs() buffering.";
        fputs(text, file);
        fclose(file);
    }
    return 0;
}

步骤及缓冲机制

  1. 打开文件:例如 fopen("example.txt", "w") 打开文件 example.txt 并返回文件指针 file

    FILE *file = fopen("example.txt", "w");
    
  2. 初始化缓冲区:标准I/O库为文件流分配一个缓冲区。默认缓冲区大小通常为 4KB 或 8KB,这取决于系统和库的实现。这个缓冲区在内部维护,程序员不需要显式分配。

  3. 调用 fputs() 写入数据

    • 调用 fputs(buffer, file)buffer 中的字符串写入到文件流 file
    • fputs() 会将字符串数据写入到文件流的缓冲区中,而不是直接写入磁盘。
    fputs(buffer, file);
    
  4. 填充缓冲区

    • fputs() 被调用时,字符串数据首先被复制到文件流的缓冲区中。
    • 如果缓冲区未满,数据会继续保留在缓冲区中,等待更多数据的写入。
  5. 写入缓冲区数据到磁盘

    • 当缓冲区被填满时,标准I/O库会将缓冲区中的数据一次性写入到磁盘,清空缓冲区以便后续写操作。
    • 这种写入操作通常由低级系统调用(如 `write())完成。
    • 即使缓冲区未满,如果程序调用 fflush()fclose(),缓冲区中的数据也会被立即写入到磁盘。
  6. 刷新缓冲区

    • 如果调用 fflush(file),会强制将缓冲区中的数据写入到磁盘,而不论缓冲区是否已满。
    fflush(file);
    
  7. 关闭文件:完成写操作后,调用 fclose(file) 关闭文件,确保缓冲区中剩余的数据写入磁盘,并释放文件流和缓冲区。

    fclose(file);
    

缓冲机制的优势

  • 减少系统调用:缓冲区机制减少了系统调用的次数。每次 fputs() 操作不会都直接导致系统调用,而是尽可能在缓冲区中积累数据,只有当缓冲区满时才进行一次实际的磁盘写操作。
  • 提高性能:通过批量写入数据到磁盘,减少了频繁的磁盘I/O操作,提高了文件写入的效率。
  • 方便使用:用户只需调用高级函数(如 fputs()),不需要关心底层的缓冲区管理和系统调用。

fputs() 写入缓冲过程

  1. 第一次调用 fputs():文件指针在文件开头,缓冲区为空。
  2. 将数据写入缓冲区fputs()text 中的字符串复制到文件流的缓冲区中。
  3. 检查缓冲区:如果缓冲区未满,数据保留在缓冲区中,等待更多数据写入。
  4. 缓冲区满时:一旦缓冲区被填满,标准I/O库将缓冲区中的数据写入磁盘,并清空缓冲区。
  5. 关闭文件:调用 fclose(file) 时,缓冲区中的剩余数据(如果有)将被写入磁盘。

这种缓冲机制确保了即使数据量很大,fputs() 也能高效地将数据写入文件,而不会因为每次小量的数据写入导致频繁的磁盘I/O操作。

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值