C++新特性之文件系统std:: filesystem,文件和目录操作

目录

一、C++ Filesystem库简介

1.1 历史背景

1.2 主要功能和用途

1.3 如何在项目中引入Filesystem库

二、文件操作基础

2.1 创建文件

2.2 读取文件

2.3 写入文件 

文件写入模式

2.4 文件属性和权限

三.  目录操作基础

3.1 创建目录

3.2 遍历目录

3.3 移动和重命名目录

3.3.1 std::filesystem::rename

3.3.2 std::filesystem::copy

3.4 删除目录

四. 路径操作

4.1 绝对路径和相对路径

4.2 路径拼接

4.3 路径解析

五、异常处理

5.1 常见错误类型

5.1.1 std::filesystem::filesystem_error

5.1.2 std::bad_alloc

5.1.3 std::invalid_argument

5.2 如何捕获和处理异常

5.2.1 使用try和catch

5.2.2 使用throw

六、实用案例

6.1 文件复制工具

6.1.1 基础复制

6.1.2 高级复制

6.2 批量重命名

6.2.1 使用std::filesystem::rename

6.2.2 批量操作

6.3 磁盘空间分析

七. 性能考虑

7.1 I/O性能

7.1.1 缓冲区(Buffering)

7.1.2 异步I/O (Asynchronous I/O)

7.2 内存映射

7.3 优化技巧

7.3.1 批量操作 (Batching)

7.3.2 方法对比表

八. 兼容性和跨平台

8.1 Windows平台

8.1.1 文件路径的特殊性

8.1.2 权限和安全性

8.2 Linux平台

8.2.1 文件系统类型

8.2.2 符号链接和硬链接

8.3 MacOS平台

8.3.1 HFS+和APFS

8.3.2 Case Sensitivity(大小写敏感性)


一、C++ Filesystem库简介

1.1 历史背景

C++ Filesystem库是C++17标准的一部分,它的出现填补了C++长久以来在文件和目录操作方面的空白。在这之前,开发者通常需要使用操作系统特定的API或第三方库。

年份发展事件
2011Boost Filesystem库发布
2014C++14标准发布,但未包含Filesystem库
2017C++17标准发布,正式引入Filesystem库

1.2 主要功能和用途

C++ Filesystem库提供了一系列用于文件和目录操作的API,包括但不限于创建、删除、复制文件和目录,以及查询文件属性等。

1.3 如何在项目中引入Filesystem库

// 引入头文件
#include <filesystem>
// 使用命名空间
namespace fs = std::filesystem;

二、文件操作基础

2.1 创建文件

        在C++中,使用std::filesystem::create_directoriesstd::filesystem::create_directory(标准文件系统库中的方法)可以轻松创建文件

#include <filesystem>
namespace fs = std::filesystem;
int main() {
    fs::create_directories("new_folder/sub_folder");
    fs::create_directory("another_folder");
}

2.2 读取文件

        读取文件通常使用std::ifstream(Input File Stream)来完成。这里的“输入”并不是说文件本身是输入,而是从程序的角度看,文件内容被“输入”到程序中。

#include <fstream>
#include <iostream>
#include <string>
int main() {
    std::ifstream file("example.txt");
    std::string line;
    while (std::getline(file, line)) {
        std::cout << line << std::endl;
    }
}

2.3 写入文件 

        写入文件在C++中通常使用std::ofstream(Output File Stream)。这里的“输出”意味着数据从程序“输出”到文件。

#include <fstream>
int main() {
    std::ofstream file("example.txt");
    file << "Hello, World!" << std::endl;
}
  • 文件写入模式

        在使用std::ofstream时,你可以选择不同的文件写入模式,例如std::ios::app(追加模式)和std::ios::trunc(截断模式)。

模式描述
std::ios::app追加到文件末尾
std::ios::trunc截断现有文件
// 追加模式
std::ofstream file("example.txt", std::ios::app);
file << "Additional line" << std::endl;

2.4 文件属性和权限

        在C++中,std::filesystem::permissionsstd::filesystem::status等函数允许你查询和修改文件属性和权限.

#include <filesystem>
namespace fs = std::filesystem;
int main() {
    fs::permissions("example.txt", fs::perms::owner_all);
}

三.  目录操作基础

3.1 创建目录

方法优点缺点
std::filesystem::create_directory简单、直接功能有限
std::filesystem::create_directories可创建多级目录相对复杂

        在C++中,使用std::filesystem::create_directory(创建目录)方法是最直接的方式来创建一个新目录。这个函数非常直观,只需要一个路径参数。

#include <filesystem>
namespace fs = std::filesystem;
int main() {
    fs::create_directory("new_directory");
}

底层原理: 实际上,这个函数会调用操作系统的API来进行目录创建。在Linux系统中,它可能会调用mkdir系统调用。

3.2 遍历目录

        使用std::filesystem::directory_iterator(目录迭代器)可以方便地遍历一个目录下的所有文件和子目录。

for(auto& p: fs::directory_iterator("some_directory")) {
    std::cout << p.path() << std::endl;
}

3.3 移动和重命名目录

3.3.1 std::filesystem::rename

        在C++中,std::filesystem::rename(重命名)函数用于重命名或移动文件和目录。这个函数接受两个参数:源路径和目标路径。

fs::rename("old_directory", "new_directory");
3.3.2 std::filesystem::copy

除了重命名,你还可以使用std::filesystem::copy(复制)函数来复制目录。

fs::copy("source_directory", "destination_directory", fs::copy_options::recursive);

这个函数实际上会递归地复制目录下的所有文件和子目录

3.4 删除目录

        删除目录在C++中是一个相对简单的操作,通常使用std::filesystem::remove(删除)函数。

fs::remove("directory_to_remove");

四. 路径操作

4.1 绝对路径和相对路径

在C++ Filesystem库中,路径(Path)是一个核心概念。路径可以分为两种:绝对路径(Absolute Path)和相对路径(Relative Path)。

  • 绝对路径:从文件系统的根目录开始,一直到目标文件或目录。
  • 相对路径:从某个特定目录开始,一直到目标文件或目录。
#include <filesystem>
namespace fs = std::filesystem;
// 绝对路径
fs::path abs_path = "/home/user/documents/file.txt";
// 相对路径
fs::path rel_path = "documents/file.txt";

4.2 路径拼接

        路径拼接(Path Concatenation)是将两个或多个路径片段合并成一个完整路径的过程。

fs::path base_path = "/home/user";
fs::path file = "documents/file.txt";
fs::path full_path = base_path / file;  // 输出:/home/user/documents/file.txt

4.3 路径解析

路径解析(Path Resolution)是将一个路径字符串解析成其各个组成部分的过程。

方法功能描述示例
parent_path()获取父路径/home/user/documents
filename()获取文件名file.txt
extension()获取文件扩展名.txt
stem()获取不带扩展名的文件名file
fs::path p = "/home/user/documents/file.txt";
std::cout << "Parent path: " << p.parent_path() << std::endl;  // 输出:/home/user/documents
std::cout << "Filename: " << p.filename() << std::endl;  // 输出:file.txt

五、异常处理

5.1 常见错误类型

在使用C++ Filesystem库时,你可能会遇到各种各样的错误。这些错误通常可以归类为以下几种:

  • std::filesystem::filesystem_error(文件系统错误)
  • std::bad_alloc(内存分配失败)
  • std::invalid_argument(无效参数)
5.1.1 std::filesystem::filesystem_error

这是最常见的错误类型,通常是因为文件或目录不存在、权限问题或磁盘空间不足等。

代码示例

try {
    std::filesystem::create_directory("some/directory");
} catch (std::filesystem::filesystem_error& e) {
    std::cerr << e.what() << std::endl;
}
5.1.2 std::bad_alloc

当系统无法分配足够的内存空间时,会抛出这种异常。

代码示例

try {
    char* myarray= new char[1000000000ul];
} catch (std::bad_alloc& e) {
    std::cerr << e.what() << std::endl;
}
5.1.3 std::invalid_argument

当传递给函数的参数不符合预期时,会抛出这种异常。

std::filesystem::path p("");
if (p.empty()) {
    throw std::invalid_argument("Path cannot be empty");
}

5.2 如何捕获和处理异常

使用trycatchthrow关键字来处理异常。

5.2.1 使用try和catch

代码示例:

try {
    // 一些可能抛出异常的代码
} catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
}
5.2.2 使用throw
if (some_condition) {
    throw std::runtime_error("Something went wrong");
}

六、实用案例

6.1 文件复制工具

6.1.1 基础复制

        在C++中,使用std::filesystem::copy(标准文件系统复制)方法可以轻松复制文件。这个函数提供了多种复制选项,如递归复制、覆盖现有文件等。

#include <filesystem>
namespace fs = std::filesystem;
int main() {
    fs::copy("source.txt", "destination.txt");
}
6.1.2 高级复制

如果你需要更多的控制,例如复制文件的元数据,你可以使用std::filesystem::copy_options(标准文件系统复制选项)。

fs::copy("source.txt", "destination.txt", fs::copy_options::overwrite_existing | fs::copy_options::recursive);

6.2 批量重命名

6.2.1 使用std::filesystem::rename

        批量重命名通常是一个繁琐的任务,但C++ Filesystem库通过std::filesystem::rename(标准文件系统重命名)方法简化了这一过程。

fs::rename("old_name.txt", "new_name.txt");
6.2.2 批量操作

        通过组合std::filesystem::directory_iterator(标准文件系统目录迭代器)和std::filesystem::rename,我们可以轻松地进行批量重命名。

for (const auto &entry : fs::directory_iterator("./my_folder")) {
    fs::rename(entry.path(), entry.path().string() + "_new");
}

6.3 磁盘空间分析

  std::filesystem::space(标准文件系统空间)方法返回一个包含磁盘空间信息的std::filesystem::space_info对象。

auto spaceInfo = fs::space("/");
std::cout << "Free space: " << spaceInfo.free << std::endl;

七. 性能考虑

7.1 I/O性能

7.1.1 缓冲区(Buffering)

在文件操作中,缓冲区(Buffering)是一个关键因素。缓冲区可以减少磁盘I/O次数,从而提高性能。当你一次读取或写入大量数据时,使用缓冲区是非常有用的。

代码示例:

#include <fstream>
#include <vector>
int main() {
    std::ofstream outfile("example.txt", std::ios::out | std::ios::binary);
    std::vector<char> buffer(1024);
    // 填充缓冲区
    outfile.write(buffer.data(), buffer.size());
    outfile.close();
}
7.1.2 异步I/O (Asynchronous I/O)

异步I/O(Asynchronous I/O)允许程序在等待I/O操作完成时继续执行其他任务。这样可以有效地利用CPU资源。

代码示例:

// C++20中的异步I/O示例
#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;
}

7.2 内存映射

内存映射(Memory Mapping)是一种将文件或文件的一部分映射到进程地址空间的方法,这样可以提高文件访问速度。

代码示例:

// 使用Boost库进行内存映射
#include <boost/iostreams/device/mapped_file.hpp>
int main() {
    boost::iostreams::mapped_file_source file("example.txt");
    auto data = file.data();
    // 直接访问内存中的数据
}

7.3 优化技巧

7.3.1 批量操作 (Batching)

批量操作(Batching)是一种将多个小操作组合成一个大操作的技术,以减少总体开销。

代码示例:

// 批量删除文件
#include <filesystem>
#include <vector>
int main() {
    std::vector<std::filesystem::path> files_to_delete = {/* ... */};
    for (const auto& file : files_to_delete) {
        std::filesystem::remove(file);
    }
}
7.3.2 方法对比表
方法优点缺点适用场景
缓冲区(Buffering)减少I/O次数,提高性能需要额外的内存大量数据读写
异步I/O充分利用CPU,提高响应性代码复杂度可能增加I/O密集型应用
内存映射快速文件访问可能增加内存使用频繁访问的大文件
批量操作减少总体开销需要一次性处理所有任务多个小操作需要合并时

八. 兼容性和跨平台

8.1 Windows平台

8.1.1 文件路径的特殊性

        在Windows平台上,文件路径通常使用反斜杠(\)作为分隔符。这与Unix和Linux系统使用正斜杠(/)不同。C++ Filesystem库在这方面做了优化,可以自动识别和处理这种差异。

#include <filesystem>
namespace fs = std::filesystem;
fs::path p1 = "C:\\Program Files";
fs::path p2 = "C:/Program Files";
8.1.2 权限和安全性

        Windows平台有着严格的文件和目录权限管理,这通常会影响到文件操作。例如,尝试访问系统文件夹通常会导致权限不足的错误。使用std::filesystem::permissions(文件系统权限)方法可以更改文件或目录的权限。

fs::permissions("example.txt", fs::perms::owner_all);

8.2 Linux平台

8.2.1 文件系统类型

Linux支持多种文件系统,如EXT4, XFS等。C++ Filesystem库在这些文件系统上都能良好地运行。使用std::filesystem::space(磁盘空间)方法可以查询文件系统的类型和可用空间。

auto spaceInfo = fs::space("/");
8.2.2 符号链接和硬链接

        Linux平台广泛使用符号链接(Symbolic Links)和硬链接(Hard Links)。std::filesystem::create_symlink(创建符号链接)和std::filesystem::create_hard_link(创建硬链接)方法用于创建链接。

fs::create_symlink("target.txt", "symlink.txt");
fs::create_hard_link("target.txt", "hardlink.txt");

8.3 MacOS平台

8.3.1 HFS+和APFS

MacOS使用HFS+或APFS作为其文件系统。C++ Filesystem库也支持这两种文件系统。与Linux平台类似,std::filesystem::space(磁盘空间)方法也适用于MacOS。

8.3.2 Case Sensitivity(大小写敏感性)

MacOS文件系统默认是不区分大小写的,这一点需要特别注意。使用std::filesystem::equivalent(等效性检查)方法可以检查两个路径是否指向同一个文件或目录。

bool isSame = fs::equivalent("file.txt", "FILE.TXT");  // 在MacOS上通常返回true

### `std::filesystem::exists` 的用途 `std::filesystem::exists` 是 C++17 中 `std::filesystem` 库提供的一个函数,用于检查指定的文件系统路径是否存在。这个路径可以是文件目录或符号链接等。如果路径存在,该函数返回 `true`,否则返回 `false`。此功能在进行文件目录操作之前非常有用,可以避免因路径不存在而导致的错误。 ```cpp #include <filesystem> #include <iostream> namespace fs = std::filesystem; int main() { fs::path p = "example.txt"; if (fs::exists(p)) { std::cout << p << " 存在。" << std::endl; } else { std::cout << p << " 不存在。" << std::endl; } return 0; } ``` ### 文件操作方法 除了 `exists` 函数外,`std::filesystem` 库还提供了多种用于文件目录操作的函数。以下是一些常用的文件操作方法: #### 1. 创建目录 - **`create_directory`**:创建单个目录。如果目录已经存在,则返回 `false`。 - **`create_directories`**:递归创建多级目录。即使路径中的某些目录已经存在,也会尝试创建所有缺失的目录。 ```cpp fs::path dir = "new_directory"; if (fs::create_directory(dir)) { std::cout << "目录 " << dir << " 创建成功。" << std::endl; } else { std::cout << "目录 " << dir << " 创建失败或已存在。" << std::endl; } fs::path multi_dir = "a/b/c"; if (fs::create_directories(multi_dir)) { std::cout << "多级目录 " << multi_dir << " 创建成功。" << std::endl; } ``` #### 2. 删除文件目录 - **`remove`**:删除指定的文件或空目录。如果删除成功,返回 `true`;否则返回 `false`。 - **`remove_all`**:递归删除指定目录及其内容,返回被成功删除的文件目录的总数。 ```cpp fs::path file = "example.txt"; if (fs::remove(file)) { std::cout << "文件 " << file << " 删除成功。" << std::endl; } else { std::cout << "文件 " << file << " 删除失败或不存在。" << std::endl; } fs::path dir = "new_directory"; uintmax_t count = fs::remove_all(dir); std::cout << "共删除了 " << count << " 个文件目录。" << std::endl; ``` #### 3. 复制文件目录 - **`copy`**:复制文件目录。如果目标路径已经存在且是一个目录,源文件目录将被复制到该目录中。 ```cpp fs::path src = "source.txt"; fs::path dest = "destination.txt"; fs::copy(src, dest); std::cout << "文件从 " << src << " 复制到 " << dest << "。" << std::endl; ``` #### 4. 获取文件属性 - **`file_size`**:获取文件的大小(以字节为单位)。 - **`last_write_time`**:获取文件目录的最后修改时间。 ```cpp fs::path p = "example.txt"; if (fs::exists(p) && fs::is_regular_file(p)) { std::cout << "文件大小: " << fs::file_size(p) << " 字节" << std::endl; auto time = fs::last_write_time(p); std::cout << "最后修改时间: " << time.time_since_epoch().count() << " 纳秒" << std::endl; } ``` #### 5. 重命名或移动文件 - **`rename`**:重命名或移动文件目录。 ```cpp fs::path old_name = "old_name.txt"; fs::path new_name = "new_name.txt"; fs::rename(old_name, new_name); std::cout << "文件从 " << old_name << " 重命名为 " << new_name << "。" << std::endl; ``` ### 总结 `std::filesystem::exists` 是一个非常实用的函数,用于检查文件目录是否存在,确保后续操作的安全性。结合 `std::filesystem` 库中的其他函数,可以轻松实现文件目录的创建、删除、复制、重命名等操作,极大地简化了文件系统的编程任务[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值