当前位置: 首页 > news >正文

`basic_filebuf`、`basic_ifstream`、`basic_ofstream`和 `basic_fstream`。

C++ 文件 I/O 模板类深度解析

文章目录

  • C++ 文件 I/O 模板类深度解析
    • 1. basic_filebuf 深度解析
      • 1.1 类模板定义详解
      • 1.2 关键成员变量
      • 1.3 核心成员函数实现原理
        • 1.3.1 open() 函数实现
        • 1.3.2 overflow() 函数实现
      • 1.4 完整示例:自定义缓冲策略
    • 2. basic_ifstream 深度解析
      • 2.1 类继承体系
      • 2.2 构造函数实现原理
      • 2.3 文件读取机制
      • 2.4 完整示例:大文件分块读取
    • 3. basic_ofstream 深度解析
      • 3.1 类继承体系
      • 3.2 文件写入优化策略
      • 3.3 完整示例:高性能日志系统
    • 4. basic_fstream 深度解析
      • 4.1 类继承体系
      • 4.2 随机访问实现原理
      • 4.3 完整示例:数据库式文件操作
    • 5. 高级主题:自定义文件系统适配器
    • 6. 性能优化技巧
      • 6.1 缓冲区大小优化
      • 6.2 内存映射文件示例

我将从底层原理到高级应用,全面详细地解析 C++ 文件 I/O 模板类,包括 basic_filebufbasic_ifstreambasic_ofstreambasic_fstream

1. basic_filebuf 深度解析

basic_filebuf是文件 I/O 的核心缓冲类,继承自 basic_streambuf

1.1 类模板定义详解

template<class CharT,                       // 字符类型 (char, wchar_t 等)class Traits = std::char_traits<CharT> // 字符特性类
> 
class basic_filebuf : public std::basic_streambuf<CharT, Traits>

1.2 关键成员变量

protected:FILE* _M_file;      // 底层C文件指针bool _M_is_open;    // 文件打开状态__c_locale _M_ctype;// 本地化信息// 缓冲区管理相关成员...

1.3 核心成员函数实现原理

1.3.1 open() 函数实现
basic_filebuf<CharT, Traits>* 
basic_filebuf<CharT, Traits>::open(const char* s, ios_base::openmode mode)
{if (is_open())return nullptr;// 转换打开模式为C风格const char* c_mode = translate_mode(mode);if (!c_mode)return nullptr;// 打开文件_M_file = fopen(s, c_mode);if (!_M_file)return nullptr;_M_is_open = true;// 初始化缓冲区_M_initialize_buffers();return this;
}
1.3.2 overflow() 函数实现

处理输出缓冲区满的情况:

typename basic_filebuf<CharT, Traits>::int_type
basic_filebuf<CharT, Traits>::overflow(int_type c)
{if (!_M_is_open)return Traits::eof();// 刷新缓冲区if (_M_write_buf_size > 0) {if (fwrite(_M_write_buf, sizeof(CharT), _M_write_buf_size, _M_file) != _M_write_buf_size)return Traits::eof();_M_write_buf_size = 0;}// 如果有额外字符,直接写入if (!Traits::eq_int_type(c, Traits::eof())) {CharT ch = Traits::to_char_type(c);if (fwrite(&ch, sizeof(CharT), 1, _M_file) != 1)return Traits::eof();}return Traits::not_eof(c);
}

1.4 完整示例:自定义缓冲策略

#include <iostream>
#include <fstream>
#include <vector>template<typename CharT, typename Traits = std::char_traits<CharT>>
class custom_filebuf : public std::basic_filebuf<CharT, Traits> {
public:using int_type = typename Traits::int_type;// 自定义缓冲区大小explicit custom_filebuf(size_t buf_size = 1024) : buffer(buf_size) {this->setp(buffer.data(), buffer.data() + buffer.size());}protected:// 重写 overflow 实现自定义刷新策略int_type overflow(int_type c) override {if (!this->is_open())return Traits::eof();// 获取当前缓冲区内容CharT* base = this->pbase();CharT* current = this->pptr();// 写入缓冲区内容if (base && current > base) {if (fwrite(base, sizeof(CharT), current - base, this->_M_file) != static_cast<size_t>(current - base))return Traits::eof();}// 处理额外字符if (!Traits::eq_int_type(c, Traits::eof())) {CharT ch = Traits::to_char_type(c);if (fwrite(&ch, sizeof(CharT), 1, this->_M_file) != 1)return Traits::eof();}// 重置缓冲区指针this->setp(buffer.data(), buffer.data() + buffer.size());return Traits::not_eof(c);}private:std::vector<CharT> buffer;
};int main() {// 使用自定义文件缓冲custom_filebuf<char> cfb;cfb.open("custom_buffer.txt", std::ios_base::out);std::ostream os(&cfb);for (int i = 0; i < 1000; ++i) {os << "Line " << i << "\n";}cfb.close();return 0;
}

2. basic_ifstream 深度解析

2.1 类继承体系

basic_istream<CharT, Traits>↑
basic_ifstream<CharT, Traits>

2.2 构造函数实现原理

template<typename CharT, typename Traits>
basic_ifstream<CharT, Traits>::basic_ifstream(const char* filename, ios_base::openmode mode): basic_istream<CharT, Traits>(&_M_filebuf),_M_filebuf()
{if (filename && !_M_filebuf.open(filename, mode | ios_base::in)) {this->setstate(ios_base::failbit);}
}

2.3 文件读取机制

template<typename CharT, typename Traits>
std::streamsize 
basic_ifstream<CharT, Traits>::readsome(CharT* s, std::streamsize n)
{// 检查流状态if (!this->good())return 0;// 获取缓冲区中可用数据std::streamsize avail = this->rdbuf()->in_avail();if (avail <= 0)return 0;// 读取不超过n的字符std::streamsize count = std::min(n, avail);return this->rdbuf()->sgetn(s, count);
}

2.4 完整示例:大文件分块读取

#include <iostream>
#include <fstream>
#include <vector>
#include <chrono>template<typename CharT>
void read_large_file(const std::basic_string<CharT>& filename, size_t chunk_size = 4096) {std::basic_ifstream<CharT> ifs(filename, std::ios_base::binary);if (!ifs) {std::basic_ostream<CharT>(std::cerr) << "Failed to open file\n";return;}auto start = std::chrono::high_resolution_clock::now();std::vector<CharT> buffer(chunk_size);size_t total_bytes = 0;while (ifs) {ifs.read(buffer.data(), chunk_size);std::streamsize count = ifs.gcount();if (count > 0) {total_bytes += count;// 处理数据块...}}auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);std::cout << "Read " << total_bytes << " bytes in " << duration.count() << " ms\n";
}int main() {// 读取大文件(4KB块)read_large_file<char>("large_file.bin");// 宽字符版本read_large_file<wchar_t>(L"large_wide_file.bin");return 0;
}

3. basic_ofstream 深度解析

3.1 类继承体系

basic_ostream<CharT, Traits>↑
basic_ofstream<CharT, Traits>

3.2 文件写入优化策略

template<typename CharT, typename Traits>
basic_ofstream<CharT, Traits>& 
basic_ofstream<CharT, Traits>::write(const CharT* s, std::streamsize n)
{// 检查流状态if (!this->good())return *this;// 尝试直接写入缓冲区std::streamsize remaining = n;while (remaining > 0) {std::streamsize avail = this->rdbuf()->sputn(s, remaining);if (avail <= 0) {this->setstate(ios_base::badbit);break;}s += avail;remaining -= avail;}return *this;
}

3.3 完整示例:高性能日志系统

#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <mutex>
#include <vector>template<typename CharT>
class ThreadSafeLogger {
public:explicit ThreadSafeLogger(const std::basic_string<CharT>& filename): _ofs(filename, std::ios_base::app) {}~ThreadSafeLogger() {std::lock_guard<std::mutex> lock(_mutex);_ofs.close();}void log(const std::basic_string<CharT>& message) {std::lock_guard<std::mutex> lock(_mutex);if (_ofs) {auto now = std::chrono::system_clock::now();auto time = std::chrono::system_clock::to_time_t(now);_ofs << std::put_time(std::localtime(&time), "%Y-%m-%d %X") << " | " << message << '\n';// 立即刷新以确保日志及时写入_ofs.flush();}}private:std::basic_ofstream<CharT> _ofs;std::mutex _mutex;
};void test_logger(ThreadSafeLogger<char>& logger, int thread_id) {for (int i = 0; i < 100; ++i) {logger.log("Thread " + std::to_string(thread_id) + " log message " + std::to_string(i));}
}int main() {ThreadSafeLogger<char> logger("thread_safe.log");std::vector<std::thread> threads;for (int i = 0; i < 10; ++i) {threads.emplace_back(test_logger, std::ref(logger), i);}for (auto& t : threads) {t.join();}return 0;
}

4. basic_fstream 深度解析

4.1 类继承体系

basic_iostream<CharT, Traits>↑
basic_fstream<CharT, Traits>

4.2 随机访问实现原理

template<typename CharT, typename Traits>
typename basic_fstream<CharT, Traits>::pos_type
basic_fstream<CharT, Traits>::seekg(pos_type pos)
{if (!this->fail()) {pos_type new_pos = this->rdbuf()->pubseekpos(pos, std::ios_base::in);if (new_pos == pos_type(-1))this->setstate(std::ios_base::failbit);return new_pos;}return pos_type(-1);
}

4.3 完整示例:数据库式文件操作

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstring>template<typename CharT>
class SimpleFileDB {struct RecordHeader {size_t id;size_t length;bool deleted;};public:explicit SimpleFileDB(const std::basic_string<CharT>& filename): _filename(filename), _next_id(1) {load_index();}~SimpleFileDB() {save_index();}size_t insert(const std::basic_string<CharT>& data) {std::basic_fstream<CharT> fs(_filename, std::ios_base::in | std::ios_base::out | std::ios_base::binary);if (!fs) {fs.open(_filename, std::ios_base::out | std::ios_base::binary);fs.close();fs.open(_filename, std::ios_base::in | std::ios_base::out | std::ios_base::binary);}RecordHeader header{_next_id++, data.length(), false};// 移动到文件末尾fs.seekp(0, std::ios_base::end);// 写入记录头fs.write(reinterpret_cast<CharT*>(&header), sizeof(header));// 写入数据fs.write(data.data(), data.length());// 更新索引_index.push_back({header.id, fs.tellp() - static_cast<std::streampos>(data.length())});return header.id;}bool get(size_t id, std::basic_string<CharT>& out) {auto it = std::find_if(_index.begin(), _index.end(), [id](const auto& entry) { return entry.first == id; });if (it == _index.end())return false;std::basic_ifstream<CharT> ifs(_filename, std::ios_base::binary);ifs.seekg(it->second);RecordHeader header;ifs.read(reinterpret_cast<CharT*>(&header), sizeof(header));if (header.deleted)return false;out.resize(header.length);ifs.read(&out[0], header.length);return true;}bool remove(size_t id) {auto it = std::find_if(_index.begin(), _index.end(), [id](const auto& entry) { return entry.first == id; });if (it == _index.end())return false;std::basic_fstream<CharT> fs(_filename, std::ios_base::in | std::ios_base::out | std::ios_base::binary);fs.seekp(it->second + offsetof(RecordHeader, deleted));bool deleted = true;fs.write(reinterpret_cast<CharT*>(&deleted), sizeof(deleted));return true;}private:void load_index() {std::basic_ifstream<CharT> ifs(_filename, std::ios_base::binary);if (!ifs)return;while (ifs) {std::streampos pos = ifs.tellg();RecordHeader header;ifs.read(reinterpret_cast<CharT*>(&header), sizeof(header));if (!ifs)break;_index.emplace_back(header.id, pos);_next_id = std::max(_next_id, header.id + 1);// 跳过数据ifs.seekg(header.length, std::ios_base::cur);}}void save_index() {// 在实际应用中,可以保存索引到单独文件}std::basic_string<CharT> _filename;std::vector<std::pair<size_t, std::streampos>> _index;size_t _next_id;
};int main() {SimpleFileDB<char> db("simple_db.dat");size_t id1 = db.insert("First record data");size_t id2 = db.insert("Second record data");std::string data;if (db.get(id1, data)) {std::cout << "Record " << id1 << ": " << data << "\n";}db.remove(id2);if (!db.get(id2, data)) {std::cout << "Record " << id2 << " not found (deleted)\n";}return 0;
}

5. 高级主题:自定义文件系统适配器

#include <iostream>
#include <streambuf>
#include <vector>
#include <memory>// 内存文件系统适配器
template<typename CharT, typename Traits = std::char_traits<CharT>>
class memory_filebuf : public std::basic_streambuf<CharT, Traits> {
public:using int_type = typename Traits::int_type;explicit memory_filebuf(std::vector<CharT>& storage): _storage(storage), _pos(0) {this->setg(_storage.data(), _storage.data(), _storage.data() + _storage.size());this->setp(_storage.data(), _storage.data() + _storage.size());}protected:// 读取操作int_type underflow() override {if (this->gptr() == this->egptr()) {if (_pos >= _storage.size())return Traits::eof();this->setg(_storage.data(), _storage.data() + _pos, _storage.data() + _storage.size());}return Traits::to_int_type(*this->gptr());}// 写入操作int_type overflow(int_type c) override {if (!Traits::eq_int_type(c, Traits::eof())) {if (_pos >= _storage.size()) {_storage.push_back(Traits::to_char_type(c));} else {_storage[_pos] = Traits::to_char_type(c);}_pos++;return c;}return Traits::not_eof(c);}// 定位操作std::streampos seekpos(std::streampos pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override {if (pos < 0 || pos > static_cast<std::streampos>(_storage.size()))return -1;_pos = static_cast<size_t>(pos);if (which & std::ios_base::in) {this->setg(_storage.data(), _storage.data() + _pos, _storage.data() + _storage.size());}if (which & std::ios_base::out) {this->setp(_storage.data() + _pos, _storage.data() + _storage.size());}return pos;}private:std::vector<CharT>& _storage;size_t _pos;
};template<typename CharT>
class memory_file : public std::basic_iostream<CharT> {
public:explicit memory_file(std::vector<CharT>& storage): std::basic_iostream<CharT>(new memory_filebuf<CharT>(storage)),_buf(static_cast<memory_filebuf<CharT>*>(this->rdbuf())) {}~memory_file() {this->rdbuf(nullptr);delete _buf;}private:memory_filebuf<CharT>* _buf;
};int main() {std::vector<char> storage;memory_file<char> mfile(storage);// 写入数据mfile << "Hello, Memory File!\n";mfile << "This is a test.\n";// 读取数据mfile.seekg(0);std::string line;while (std::getline(mfile, line)) {std::cout << line << "\n";}return 0;
}

6. 性能优化技巧

6.1 缓冲区大小优化

template<typename CharT>
void optimize_buffer_size(const std::basic_string<CharT>& filename) {// 测试不同缓冲区大小对性能的影响const size_t file_size = 100 * 1024 * 1024; // 100MBconst std::vector<size_t> buffer_sizes = {512, 1024, 4096, 8192, 16384, 65536};// 创建测试文件{std::basic_ofstream<CharT> ofs(filename, std::ios_base::binary);std::vector<CharT> data(file_size, 'A');ofs.write(data.data(), data.size());}// 测试不同缓冲区大小for (size_t buf_size : buffer_sizes) {std::vector<CharT> buffer(buf_size);auto start = std::chrono::high_resolution_clock::now();std::basic_ifstream<CharT> ifs(filename, std::ios_base::binary);ifs.rdbuf()->pubsetbuf(buffer.data(), buf_size);size_t total_read = 0;while (ifs) {ifs.read(buffer.data(), buf_size);total_read += ifs.gcount();}auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);std::cout << "Buffer size: " << buf_size << " bytes, Time: " << duration.count() << " ms\n";}
}

6.2 内存映射文件示例

#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endiftemplate<typename CharT>
class memory_mapped_file {
public:explicit memory_mapped_file(const std::basic_string<CharT>& filename) {
#ifdef _WIN32_hFile = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (_hFile == INVALID_HANDLE_VALUE)throw std::runtime_error("Failed to open file");_hMapping = CreateFileMapping(_hFile, NULL, PAGE_READONLY, 0, 0, NULL);if (_hMapping == NULL) {CloseHandle(_hFile);throw std::runtime_error("Failed to create file mapping");}_data = static_cast<const CharT*>(MapViewOfFile(_hMapping, FILE_MAP_READ, 0, 0, 0));if (_data == NULL) {CloseHandle(_hMapping);CloseHandle(_hFile);throw std::runtime_error("Failed to map view of file");}DWORD sizeHigh;_size = GetFileSize(_hFile, &sizeHigh);_size |= (static_cast<uint64_t>(sizeHigh) << 32);
#else_fd = open(filename.c_str(), O_RDONLY);if (_fd == -1)throw std::runtime_error("Failed to open file");struct stat sb;if (fstat(_fd, &sb) == -1) {close(_fd);throw std::runtime_error("Failed to get file size");}_size = sb.st_size;_data = static_cast<const CharT*>(mmap(NULL, _size, PROT_READ, MAP_PRIVATE, _fd, 0));if (_data == MAP_FAILED) {close(_fd);throw std::runtime_error("Failed to map file");}
#endif}~memory_mapped_file() {
#ifdef _WIN32UnmapViewOfFile(_data);CloseHandle(_hMapping);CloseHandle(_hFile);
#elsemunmap(const_cast<CharT*>(_data), _size);close(_fd);
#endif}const CharT* data() const { return _data; }size_t size() const { return _size; }private:const CharT* _data;size_t _size;
#ifdef _WIN32HANDLE _hFile;HANDLE _hMapping;
#elseint _fd;
#endif
};int main() {try {memory_mapped_file<char> mmf("large_file.bin");std::cout << "File size: " << mmf.size() << " bytes\n";// 可以直接访问文件内容,无需读取操作const char* data = mmf.data();// 处理数据...} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << "\n";}return 0;
}

以上内容全面深入地解析了 C++ 文件 I/O 模板类的各个方面,从基础用法到高级特性,从性能优化到自定义实现,涵盖了文件操作的所有关键知识点。

http://www.dtcms.com/a/358116.html

相关文章:

  • 【高级机器学习】 4. 假设复杂度与泛化理论详解
  • 【超全汇总】MySQL服务启动命令手册(Linux+Windows+macOS)(上)
  • React前端开发_Day10
  • 针对 “TCP 连接建立阶段” 的攻击
  • PAT 1088 Rational Arithmetic
  • android adb调试 鸿蒙
  • 微信小程序长按识别图片二维码
  • mysql的内置函数
  • psql介绍(PostgreSQL命令行工具)(pgAdmin内置、DBeaver、Azure Data Studio)数据库命令行工具
  • 三数之和,leetCode热题100,C++实现
  • Ubuntu 中通过 SSH 克隆 Windows 上的 Git 仓库
  • C++转置正方形矩阵
  • components.d.ts声明组件类型的作用
  • LeetCode100-240搜索二维矩阵Ⅱ
  • linux基础——UDP、TCP
  • 北斗导航 | RAIM算法改进方案及性能对比分析报告
  • 力扣(LeetCode) ——645. 错误的集合(C语言)
  • 算法(③二叉树)
  • 精简版UDP网络编程:Socket套接字应用
  • 网格纹理采样算法
  • U盘/移动硬盘里可清理的那些跨系统遗留文件
  • 使用JAVA制作minecraft红石和创造模式插件
  • 理解JVM
  • 蓝牙5.3核心技术架构解析:从控制器到主机的无线通信设计
  • 广东省省考备考(第九十天8.30)——判断推理(强化训练)
  • 项目管理的五个阶段是什么
  • 在线简历生成工具,免费好用
  • 【MLLM】从BLIP3o到BLIP3o-NEXT:统一生成与理解
  • 【Docker】Docker初识
  • AI工具营销落地方案:工业产品营销