需求
项目中需要对文件进行curd,或者对文件信息进行输出打印等操作。本文根据参考文献进行修改,主题是简洁,低代码。筛选关键的代码。详细可看参考文献。
理论
C++17 中引入了 std::filesystem 库,它提供了一组用于操作文件系统的类和函数。这个库使得我们在 C++ 中处理文件和目录变得更加简单和跨平台。
历史背景
C++ Filesystem库是C++17标准的一部分,它的出现填补了C++长久以来在文件和目录操作方面的空白。在这之前,开发者通常需要使用操作系统特定的API或第三方库。现在不需要寻找特定三方库嵌入到项目中,太麻烦了,支持c17标准就能实现。
年份 | 发展事件 |
2011 | Boost Filesystem库发布 |
2014 | C++14标准发布,但未包含Filesystem库 |
2017 | C++17标准发布,正式引入Filesystem库 |
技术对比
特性 | C++ Filesystem | 传统C++ I/O | Python I/O |
代码简洁性 | 高 | 中 | 高 |
性能 | 高 | 高 | 中 |
平台兼容性 | 高 | 低 | 高 |
实践
如何在项目中引入Filesystem库
// 引入头文件
#include <filesystem>
// 使用命名空间
namespace fs = std::filesystem;
curd操作
读写文件我们单独演示,因为比较重要
路劲操作:
拼接:
full_path = base_path / file; 输出:/home/user/documents/file.txt
解析:
fs::path p = "/home/user/documents/file.txt";
p.parent_path() 输出:/home/user/documents
p.filename() 输出:file.txt
p.extension() 输出:.txt
p.stem() 输出:file
文件和目录操作:
create_directory 创建目录
create_directories 创建多级目录
exists 检查文件或目录是否存在。
is_regular_file 和 is_directory 等函数检查文件类型。
copy 和 copy_file 复制文件或目录。
选项:
copy_options::update_existing: 如果 to_path 已存在且更新时间较新,则不复制。
copy_options::recursive: 递归复制目录及其子项。
copy_options::overwrite_existing: 始终覆盖现有文件。
copy("source.txt", "destination.txt", fs::copy_options::overwrite_existing | fs::copy_options::recursive);
remove 和 remove_all 删除文件或目录。
rename 文件重命名
文件属性获取:
file_size 获取文件大小。
last_write_time 获取文件最后修改时间。
status 获取文件状态信息,包括权限、所有者等。
目录遍历: 在遍历目录时,你可以选择深度优先或广度优先。深度优先通常用于搜索操作,而广度优先则更适用于快速查找。
directory_iterator 用广度优先遍历目录中的文件和子目录。
recursive_directory_iterator 用于深度优先递归遍历目录树。
综合运用
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
const std::string directory_path= "/path/to/your/directory";
for (const auto& entry : fs::directory_iterator(directory_path)) {
if (fs::is_regular_file(entry)) {
std::cout << "文件名: " << entry.path().filename() << ", 大小: " << fs::file_size(entry) << " bytes" << std::endl;
}
}
return 0;
}
文件读写
文件读取和写操作 一般配合
I/O 操作来读取文件内容即ifstream和ofstream
模式
标准输入/输出模式:
std::ifstream: 用于从文件中读取数据。
std::ofstream: 用于向文件中写入数据。
std::fstream: 可以同时读取和写入文件。
文件打开模式:
std::ios::in: 以只读模式打开文件。
std::ios::out: 以只写模式打开文件。
std::ios::app: 以追加模式打开文件,写入数据时会添加到文件末尾。
std::ios::trunc: 以截断模式打开文件,如果文件存在,则会清空文件内容。
std::ios::binary: 以二进制模式打开文件,不进行任何文本格式转换。
文件定位模式:
std::ios::beg: 从文件开头开始定位。
std::ios::cur: 从当前位置开始定位。
std::ios::end: 从文件末尾开始定位。
举例:
std::ofstream file("example.txt", std::ios::app);
高端操作
后续展示,先做个总结。
方法 | 优点 | 缺点 | 适用场景 |
缓冲区(Buffering) | 减少I/O次数,提高性能 | 需要额外的内存 | 大量数据读写 |
异步I/O | 充分利用CPU,提高响应性 | 代码复杂度可能增加 | I/O密集型应用 |
内存映射 | 快速文件访问 | 可能增加内存使用 | 频繁访问的大文件 |
批量操作 | 减少总体开销 | 需要一次性处理所有任务 | 多个小操作需要合并时 |
读取文件
基本操作
std::ifstream file("example.txt");
std::string line;
while (std::getline(file, line)) {
std::cout << line << std::endl;
}
异步IO读
#include <iostream>
#include <fstream>
#include <future>
void async_read(const std::string& file_name) {
std::ifstream infile(file_name, std::ios::in);
std::string content;
// 异步读取文件
auto future = std::async(std::launch::async, [&]() {
std::getline(infile, content);
});
// 在这里可以做其他事情
future.wait();
std::cout << "File content: " << content << std::endl;
}
文件内存映射
// 使用Boost库进行内存映射
#include <boost/iostreams/device/mapped_file.hpp>
int main() {
boost::iostreams::mapped_file_source file("example.txt");
auto data = file.data();
// 直接访问内存中的数据
}
写入文件
写入缓冲区
std::ofstream outfile("example.txt", std::ios::out | std::ios::binary);
std::vector<char> buffer(1024);
// 填充缓冲区
outfile.write(buffer.data(), buffer.size());
outfile.close();
批量删除
std::vector<std::filesystem::path> files_to_delete = {/* ... */};
for (const auto& file : files_to_delete) {
std::filesystem::remove(file);
}
错误处理和异常
三种常见异常
std::filesystem::filesystem_error
(文件系统错误)std::bad_alloc
(内存分配失败)std::invalid_argument
(无效参数)
处理和捕获
当文件或目录不存在和权限不足
try {
std::filesystem::remove("/path/to/non/existent/file");
} catch (const std::filesystem::filesystem_error& e) {
std::cerr << "错误: " << e.what() << std::endl;
}
当系统无法分配足够的内存空间时,会抛出这种异常。 近期微软蓝屏事件bug
try {
char* myarray= new char[1000000000ul];
} catch (std::bad_alloc& e) {
std::cerr << e.what() << std::endl;
}
自定义抛出信息
try {
std::filesystem::path p("");
if (p.empty()) {
throw std::invalid_argument("Path cannot be empty");
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
方法 | 优点 | 缺点 |
使用标准异常 | 代码可读性好,易于维护 | 可能不满足特殊需求 |
不要滥用异常 | 提高程序效率 | 需要更多的错误检查代码 |
清理资源 | 避免内存泄漏 | 需要额外的代码 |
参考文献
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南(一)-阿里云开发者社区 (aliyun.com)
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南(二)-阿里云开发者社区 (aliyun.com)
总结
学习和使用filesystem有利于我们开发出跨平台的应用,而且函数封装的很好,值得我们学习,以最少的时间,做出正确的效果。
参考文献写的很详细,如果读取本文还有不懂得,可以进入文献,进一步的学习,开拓视野。