std::ifstream read函数读取细节

Source

  file.read(buffer, CHUNK_SIZE) 是 C++ 标准库中的 std::ifstream 类的成员函数之一,它用于从文件流中读取一块数据到缓冲区 buffer 中。底层的实现涉及文件流的管理、文件指针的移动、内存操作等多个方面。

file.read(buffer, CHUNK_SIZE) 的调用流程

a. 检查文件状态
  • file.read() 会首先检查流的状态,以确保流处于良好的状态(即没有遇到 EOF、错误等情况),并且文件可以被读取。
  • 如果流的状态不良(如已经到达 EOF),read 不会进行进一步操作。
b. 读取数据到缓冲区
  • 内存分配与传输

    • buffer 是由调用者提供的一个预分配好的缓冲区。file.read() 会将文件中的数据读取到这个缓冲区中。
    • 底层通过操作系统提供的文件 I/O 函数(如 POSIX 中的 read 系统调用)从文件中读取数据块,并将这些数据复制到内存缓冲区 buffer 中。
  • 文件指针的移动

    • 操作系统负责管理文件指针。在调用 read 系统调用后,文件指针会自动从当前的位置向前移动读取的字节数,即 CHUNK_SIZE 字节。这是因为文件指针的管理是由操作系统内部实现的,文件的每次读写操作都会更新文件指针的位置。
c. 更新文件流状态
  • gcount() 的更新

    • gcount()std::istream 类的成员函数,它返回上一次读取操作成功读取的字节数。在 file.read() 操作结束后,gcount() 的值会被更新为实际读取的字节数。
    • 如果文件中的数据不足 CHUNK_SIZEgcount() 会小于 CHUNK_SIZE。如果刚好读取到文件的末尾,gcount() 会反映出最后读取的数据大小。
  • 设置流状态

    • 如果读取操作遇到文件结尾,read() 会设置流的 eofbit(表示到达文件末尾)。
    • 如果读取失败或发生其他错误(如硬盘故障),failbitbadbit 可能会被设置。
d. 返回流对象
  • file.read() 返回对自身对象的引用(即流对象本身),以便支持链式调用。

#include <iostream>
#include <fstream>
#include <iomanip>

int main() {
    const char* filename = "large_file.bin"; // 替换为你的文件路径
    const std::streamsize CHUNK_SIZE = 1024; // 每次读取 1KB

    // 打开文件
    std::ifstream file(filename, std::ios::binary);

    if (!file) {
        std::cerr << "Failed to open file: " << filename << std::endl;
        return 1;
    }

    char buffer[CHUNK_SIZE];
    std::streamsize bytesRead = 0;
    std::streampos startPos = 0;

    while (true) {
        // 读取前显示当前文件指针位置
        startPos = file.tellg();
        std::cout << "Current file position before read: " << startPos << std::endl;

        // 读取数据到缓冲区
        file.read(buffer, CHUNK_SIZE);
        bytesRead = file.gcount(); // 获取实际读取的字节数

        // 读取后的文件指针位置
        std::streampos endPos = file.tellg();

        std::cout << "Bytes read: " << bytesRead << std::endl;
        std::cout << "New file position after read: " << endPos << std::endl;

        // 输出读取的数据(显示前16个字节的十六进制表示)
        std::cout << "Data (first 16 bytes in hex): ";
        for (std::streamsize i = 0; i < std::min(bytesRead, static_cast<std::streamsize>(16)); ++i) {
            std::cout << std::hex << std::setw(2) << std::setfill('0')
                      << static_cast<int>(static_cast<unsigned char>(buffer[i])) << " ";
        }
        std::cout << std::dec << std::endl;

        // 如果到达文件末尾,退出循环
        if (bytesRead < CHUNK_SIZE) {
            std::cout << "Reached end of file or read error." << std::endl;
            break;
        }
    }

    // 关闭文件
    file.close();

    return 0;
}

代码说明:

  1. 文件打开与检查

    • 程序首先尝试打开一个文件 large_file.bin。如果文件无法打开,程序会输出错误信息并退出。
  2. 文件读取循环

    • file.tellg():在每次读取之前调用,获取当前文件指针的位置(即将要读取的位置)。
    • file.read(buffer, CHUNK_SIZE):从文件中读取最多 CHUNK_SIZE 字节的数据到 buffer 中。
    • file.gcount():读取完成后,获取实际读取的字节数。如果文件中剩余的数据不足 CHUNK_SIZEgcount() 会返回实际读取的字节数。
    • file.tellg():读取完成后再次调用,获取新的文件指针位置。
  3. 调试输出

    • 输出每次读取前后的文件指针位置、实际读取的字节数以及前16个字节的数据(以十六进制表示)。
  4. 循环终止条件

    • 如果实际读取的字节数小于 CHUNK_SIZE,表明文件已经读取完毕或发生了读取错误,程序会输出信息并退出循环。