C++17 filesystem 库 学习笔记
- Filesystem library
- 1、类
- 2、非成员函数
- 2.1、std::filesystem::absolute
- 2.2、std::filesystem::canonical, std::filesystem::weakly_canonical
- 2.3、std::filesystem::relative, std::filesystem::proximate
- 2.4、std::filesystem::copy
- 2.5、std::filesystem::copy_file
- 2.6、std::filesystem::copy_symlink
- 2.7、std::filesystem::create_directory, std::filesystem::create_directories
- 2.8、std::filesystem::create_hard_link
- 2.9、std::filesystem::create_symlink, std::filesystem::create_directory_symlink
- 2.10、std::filesystem::current_path
- 2.11、std::filesystem::exists
- 2.12、std::filesystem::equivalent
- 2.13、std::filesystem::file_size
- 2.14、std::filesystem::hard_link_count
- 2.15、std::filesystem::last_write_time
- 2.16、std::filesystem::permissions
- 2.17、std::filesystem::read_symlink
- 2.18、std::filesystem::remove, std::filesystem::remove_all
- 2.19、std::filesystem::rename
- 2.20、std::filesystem::resize_file
- 2.21、std::filesystem::space
- 2.22、std::filesystem::status, std::filesystem::symlink_status
- 2.23、std::filesystem::temp_directory_path
- 3、文件类型
- 3.1、std::filesystem::is_block_file
- 3.2、std::filesystem::is_character_file
- 3.3、std::filesystem::is_directory
- 3.4、std::filesystem::is_empty
- 3.5、std::filesystem::is_fifo
- 3.6、std::filesystem::is_other
- 3.7、std::filesystem::is_regular_file
- 3.8、std::filesystem::is_socket
- 3.9、std::filesystem::is_symlink
- 3.10、std::filesystem::status_known
Filesystem library
官方文档:https://en.cppreference.com/w/cpp/filesystem
Filesystem 由 boost.filesystem 发展而来,自 C++17 开始引入到 ISO C++ 中,用于操作文件系统及其组件(路径、常规文件及文件夹等)。如果希望在较低版本的 C++ 编译器中使用 filesystem,需要换用 boost。
1、类
- 定义于头文件
<filiesystem>
- 定义于命名空间
std::filesystem
这里面的各类不再一一列举,它们的功能直接通过下面表格里的链接跳转到官方页面即可。
path | 路径 |
filesystem_error | 文件系统错误异常 |
directory_entry | 文件夹 |
directory_iterator | 文件夹内容的迭代器 |
recursive_directory_iterator | 递归式迭代指定路径下的所有文件及文件夹 |
file_status | 文件类型及权限 |
space_info | 文件系统中可用的剩余空间信息 |
file_type | 文件类型 |
perms | 文件系统权限标识符 |
perm_options | 指明权限操作的语义 |
copy_options | 指明复制操作的语义 |
directory_options | 对文件夹内容进行迭代时的选项 |
file_time_type | 文件时间信息 |
2、非成员函数
- 定义于头文件
<filiesystem>
- 定义于命名空间
std::filesystem
absolute | 绝对路径 |
canonical、weakly_canonical | 将相对路径转为绝对路径 |
relative、proximate | 相对路径 |
copy | 复制文件或者文件夹 |
copy_file | 复制文件内容 |
copy_symlink | 复制符号链接 |
create_directory、create_directories | 创建新文件夹 |
create_hard_link | 创建硬链接 |
create_symlink、create_directory_symlink | 创建符号链接 |
current_path | 返回或修改当前工作路径 |
exists | 判断路径是否存在 |
equivalent | 判断两个路径是否相同 |
file_size | 返回文件大小 |
hard_link_count | 返回指定文件的硬链接数量 |
last_write_time | 获取或设置最近一次修改数据的时间 |
permissions | 修改访问权限 |
read_symlink | 获取符号链接的目标 |
remove、remove_all | 删除文件或空文件夹,后者可递归式删除非空文件夹 |
rename | 重命名文件或文件夹 |
resize_file | 改变常规文件的大小 |
space | 确定文件系统中的可用剩余空间 |
status、symlink_status | 确定文件属性;检查符号链接目标 |
temp_directory_path | 返回一个存放临时文件的文件夹 |
2.1、std::filesystem::absolute
定义于
<filesystem>
返回包含 p 在内的绝对路径
path absolute( const std::filesystem::path& p );
path absolute( const std::filesystem::path& p, std::error_code& ec );
异常:
如果内存分配失败,任何未标记为 noexcept 的重载函数都可能引发 std::bad_alloc
;
第一个函数在底层系统 API 错误上抛出 std::filesystem::filesystem_error
,构建时将 p 作为第一个路径参数,将系统错误代码作为错误代码参数。
如果系统 API 调用失败,第二个函数会将 std::error_code& 参数设置为系统 API 错误代码;如果未出现错误,则执行 ec.clear()。
注意:
对 p 不存在的情况,并非异常;
对基于 POSIX 的系统来说,std::filesystem::absolute(p)
等价于 std::filesystem::current_path() / p
;
对 Windows 来说,std::filesystem::absolute
可能是基于 GetFullPathNameW
开发的。
案例:
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
std::filesystem::path p = "foo.c";
std::cout << "Current path is " << std::filesystem::current_path() << '\n';
std::cout << "Absolute path for " << p << " is " << fs::absolute(p) << '\n';
}
输出:
Current path is "/tmp/1666297965.0051296"
Absolute path for "foo.c" is "/tmp/1666297965.0051296/foo.c"
2.2、std::filesystem::canonical, std::filesystem::weakly_canonical
定义于
<filesystem>
将 p 转换为规范的绝对路径,这两个函数的差异可以详见后面的案例
path canonical( const std::filesystem::path& p );
path canonical( const std::filesystem::path& p,
std::error_code& ec );
path weakly_canonical( const std::filesystem::path& p );
path weakly_canonical( const std::filesystem::path& p,
std::error_code& ec );
异常:
如果内存分配失败,任何未标记为 noexcept 的重载函数都可能引发 std::bad_alloc
;
不带 ec 的几个函数在底层系统 API 错误上抛出 std::filesystem::filesystem_error
,构建时将 p 作为第一个路径参数,将系统错误代码作为错误代码参数。
如果系统 API 调用失败,另外几个函数会将 std::error_code& 参数设置为系统 API 错误代码;如果未出现错误,则执行 ec.clear()。
案例:
#include <filesystem>
#include <iostream>
int main()
{
/* set up sandbox directories:
a
└── b
├── c1
│ └── d <== current path
└── c2
└── e
*/
auto old = std::filesystem::current_path();
auto tmp = std::filesystem::temp_directory_path();
std::filesystem::current_path(tmp);
auto d1 = tmp / "a/b/c1/d";
auto d2 = tmp / "a/b/c2/e";
std::filesystem::create_directories(d1);
std::filesystem::create_directories(d2);
std::filesystem::current_path(d1);
auto p1 = std::filesystem::path("../../c2/./e");
auto p2 = std::filesystem::path("../no-such-file");
std::cout << "Current path is "
<< std::filesystem::current_path() << '\n'
<< "Canonical path for " << p1 << " is "
<< std::filesystem::canonical(p1) << '\n'
<< "Weakly canonical path for " << p2 << " is "
<< std::filesystem::weakly_canonical(p2) << '\n';
try
{
[[maybe_unused]] auto x_x = std::filesystem::canonical(p2);
// NOT REACHED
}
catch (const std::exception& ex)
{
std::cout << "Canonical path for " << p2 << " threw exception:\n"
<< ex.what() << '\n';
}
// cleanup
std::filesystem::current_path(old);
const auto count = std::filesystem::remove_all(tmp / "a");
std::cout << "Deleted " << count << " files or directories.\n";
}
输出:
Current path is "/tmp/a/b/c1/d"
Canonical path for "../../c2/./e" is "/tmp/a/b/c2/e"
Weakly canonical path for "../no-such-file" is "/tmp/a/b/c1/no-such-file"
Canonical path for "../no-such-file" threw exception:
filesystem error: in canonical: No such file or directory [../no-such-file] [/tmp/a/b/c1/d]
Deleted 6 files or directories.
2.3、std::filesystem::relative, std::filesystem::proximate
定义于
<filesystem>
返回相对路径,具体使用方法见后面的案例
path relative( const std::filesystem::path& p,
std::error_code& ec );
path relative( const std::filesystem::path& p,
const std::filesystem::path& base = std::filesystem::current_path() );
path relative( const std::filesystem::path& p,
const std::filesystem::path& base,
std::error_code& ec );
path proximate( const std::filesystem::path& p,
std::error_code& ec );
path proximate( const std::filesystem::path& p,
const std::filesystem::path& base = std::filesystem::current_path() );
path proximate( const std::filesystem::path& p,
const std::filesystem::path& base,
std::error_code& ec );
异常:
如果内存分配失败,任何未标记为 noexcept 的重载函数都可能引发 std::bad_alloc
;
不带 ec 参数的函数在底层系统 API 错误上抛出 std::filesystem::filesystem_error
,构建时将 p 作为第一个路径参数,将系统错误代码作为错误代码参数。
如果系统 API 调用失败,另外几个函数会将 std::error_code& 参数设置为系统 API 错误代码;如果未出现错误,则执行 ec.clear()。
案例:
#include <filesystem>
#include <iostream>
void show(std::filesystem::path x, std::filesystem::path y)
{
std::cout << "x:\t\t " << x << "\ny:\t\t " << y << '\n'
<< "relative(x, y): "
<< std::filesystem::relative(x, y) << '\n'
<< "proximate(x, y): "
<< std::filesystem::proximate(x, y) << "\n\n";
}
int main()
{
show("/a/b/c", "/a/b");
show("/a/c", "/a/b");
show("c", "/a/b");
show("/a/b", "c");
}
输出:
x: "/a/b/c"
y: "/a/b"
relative(x, y): "c"
proximate(x, y): "c"
x: "/a/c"
y: "/a/b"
relative(x, y): "../c"
proximate(x, y): "../c"
x: "c"
y: "/a/b"
relative(x, y): ""
proximate(x, y): "c"
x: "/a/b"
y: "c"
relative(x, y): ""
proximate(x, y): "/a/b"
2.4、std::filesystem::copy
定义于
<filesystem>
可以以指定复制方案的方式,对文件或者文件夹进行复制
void copy( const std::filesystem::path& from,
const std::filesystem::path& to );
void copy( const std::filesystem::path& from,
const std::filesystem::path& to,
std::error_code& ec );
void copy( const std::filesystem::path& from,
const std::filesystem::path& to,
std::filesystem::copy_options options );
void copy( const std::filesystem::path& from,
const std::filesystem::path& to,
std::filesystem::copy_options options,
std::error_code& ec );
案例:
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::create_directories("sandbox/dir/subdir");
std::ofstream("sandbox/file1.txt").put('a');
fs::copy("sandbox/file1.txt", "sandbox/file2.txt"); // copy file
fs::copy("sandbox/dir", "sandbox/dir2"); // copy directory (non-recursive)
const auto copyOptions = fs::copy_options::update_existing
| fs::copy_options::recursive
| fs::copy_options::directories_only
;
fs::copy("sandbox", "sandbox_copy", copyOptions);
static_cast<void>(std::system("tree"));
fs::remove_all("sandbox");
fs::remove_all("sandbox_copy");
}
输出:
.
├── sandbox
│ ├── dir
│ │ └── subdir
│ ├── dir2
│ ├── file1.txt
│ └── file2.txt
└── sandbox_copy
├── dir
│ └── subdir
└── dir2
8 directories, 2 files
异常的情况与其他函数基本相同,后面不再赘述。
函数在执行时的具体行为
- 首先,在执行任何其他操作之前,只需对下面的两个函数进行一次调用,即可获得
from
的类型和权限:-
std::filesystem::symlink_status
(如果在 options 中指定了copy_options::skip_symlinks
,copy_options::copy_symlinks
, 或copy_options::create_symlinks
) -
std::filesystem::status
(其他情况)
-
- 如果有必要,只需对下面的两个函数进行一次调用,即可获得
to
的状态:-
std::filesystem::symlink_status
(如果在 options 中指定了copy_options::skip_symlinks
, 或copy_options::create_symlinks
)) -
std::filesystem::status
(包含在 options 中指定copy_options::copy_symlinks
在内的其他情况)
-
- 如果 from 或 to 中有 implementation-defined 文件类型,则函数的执行效果也是 implementation-defined;
- 如果 from 不存在,函数将报错;
- 如果 from 与 to 是相同的,函数将报错(具体来说,两者是通过函数
std::filesystem::equivalent
判定是否相同的); - 如果 from 或 to 不是常规文件、文件夹或者符号链接,则函数报错(通过函数
std::filesystem::is_other
判定的); - 如果 from 是文件夹,to 是常规文件,则函数报错;
- 如果 from 是符号链接,则有(以下三条,优先级逐渐降低):
- 如果 options 中有
copy_options::skip_symlink
,则函数啥也不干 - 否则,如果 to 不存在,且 options 中有
copy_options::copy_symlinks
,则函数的行为与copy_symlink(from, to)
一致; - 对不含上面情况的其他情形,函数报错;
- 如果 options 中有
- 如果 from 是常规文件,则有(以下五条,优先级逐渐降低):
- 如果设置 options 为
copy_options::directories_only
,则函数啥也不干; - 否则,如果 options 中有
copy_options::create_symlinks
,函数创建一个符号链接(from 必须为绝对路径) - 否则,如果 options 中有
copy_options::create_hard_links
,函数创建一个硬链接 - 否则,如果 to 是文件夹,那函数的行为与
copy_file(from, to/from.filename(), options)
相同(即在 to 中创建一个文件) - 否则,与函数
copy_file(from, to, options)
的表现相同。
- 如果设置 options 为
- 如果 from 是文件夹,并且在 options 中设置了
copy_options::create_symlinks
,函数将会报错,相应的错误代码为std::make_error_code(std::errc::is_a_directory)
; - 如果 from 是文件夹,而 options 中设置了
copy_options::recursive
或copy_options::none
,则:- 如果 to 不存在,函数将先执行
create_directory(to, from)
, - 然后,在 from 中含有的文件中进行迭代(使用了
for (const std::filesystem::directory_entry& x : std::filesystem::directory_iterator(from))
),并在每个子文件夹中递归地调用copy(x.path(), to/x.path().filename(), options | in-recursive-copy)
- 如果 to 不存在,函数将先执行
- 对 from 与 to 的其他情况,函数什么也不做。
2.5、std::filesystem::copy_file
定义于
<filesystem>
可以以指定复制方案的方式,对文件进行复制
bool copy_file( const std::filesystem::path& from,
const std::filesystem::path& to );
bool copy_file( const std::filesystem::path& from,
const std::filesystem::path& to,
std::error_code& ec );
bool copy_file( const std::filesystem::path& from,
const std::filesystem::path& to,
std::filesystem::copy_options options );
bool copy_file( const std::filesystem::path& from,
const std::filesystem::path& to,
std::filesystem::copy_options options,
std::error_code& ec );
案例:
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::create_directory("sandbox");
std::ofstream("sandbox/file1.txt").put('a');
fs::copy_file("sandbox/file1.txt", "sandbox/file2.txt");
// now there are two files in sandbox:
std::cout << "file1.txt holds: "
<< std::ifstream("sandbox/file1.txt").rdbuf() << '\n';
std::cout << "file2.txt holds: "
<< std::ifstream("sandbox/file2.txt").rdbuf() << '\n';
// fail to copy directory
fs::create_directory("sandbox/abc");
try
{
fs::copy_file("sandbox/abc", "sandbox/def");
}
catch (fs::filesystem_error& e)
{
std::cout << "Could not copy sandbox/abc: " << e.what() << '\n';
}
fs::remove_all("sandbox");
}
输出:
file1.txt holds: a
file2.txt holds: a
Could not copy sandbox/abc: copy_file: Is a directory: "sandbox/abc", "sandbox/def"
函数执行时的行为
- 前两个函数相当于在后两个函数中为 options 设置
copy_options::none
; - 后两个函数中,基于 options 中的设置内容,从 from 中复制一份文件到 to 中。如果 options 中的任何copy_options 选项组中存在多个 option,则函数的行为未定义。
- 如果
!filesystem::is_regular_file(from)
为 true,则函数报错; - 否则,如果 to 不存在,
- 复制 from 的内容及属性;
- 否则,如果 to 已经存在了,
- 如果有下面的情况,函数就报错:
- to 与 from 相同(由
filesystem::equivalent(from, to)
判定) - to 不是常规文件(由
! filesystem::is_regular_file(to)
判定) - options 中没有设置控制选项
- to 与 from 相同(由
- 否则,如果在 options 中设置了
copy_options::skip_existing
,则函数什么也不做; - 否则,如果在 options 中设置了
copy_options::overwrite_existing
,执行复制操作,替换掉已有文件; - 否则,如果在 options 中设置了
copy_options::update_existing
,则仅在 from 比 to 较新的时候才会进行复制(用filesystem::last_write_time()
的执行结果判定 from 是否比 to 新)
- 如果有下面的情况,函数就报错:
- 如果
2.6、std::filesystem::copy_symlink
定义于
<filesystem>
可以以指定复制方案的方式,对符号链接进行复制
第一个函数依据 from 是文件还是文件夹,将以
f(read_symlink(from), to)
的形式调用函数create_symlink
或create_directory_symlink
;第二个函数依据 from 是文件还是文件夹,将以
f(read_symlink(from), to, ec)
的形式调用函数create_symlink
或create_directory_symlink
;
void copy_symlink( const std::filesystem::path& from,
const std::filesystem::path& to);
void copy_symlink( const std::filesystem::path& from,
const std::filesystem::path& to,
std::error_code& ec ) noexcept;
2.7、std::filesystem::create_directory, std::filesystem::create_directories
bool create_directory( const std::filesystem::path& p );
bool create_directory( const std::filesystem::path& p, std::error_code& ec ) noexcept;
bool create_directory( const std::filesystem::path& p,
const std