C++文件操作
在C++17之前,C++并没有跨平台的统一文件操作解决方案,不同的平台需要依赖不同的API或者第三方库来处理文件操作。C++17引入的std::filesystem就解决了这个痛点。
常见函数
以下按与你之前相似的格式,列出并详解 <filesystem> 中日常最常见的函数及其关键点:
fs::exists(p)- 判断路径
p是否存在(文件、目录或符号链接)。 - 返回
true或false - 不抛异常
- 比起捕获异常更轻量
- 若无权限访问,也返回
false。
- 判断路径
fs::status(p)/fs::symlink_status(p)- 获取路径的类型和权限信息。
- 返回
file_status,可用status.type()判断类型。 - 两个函数的区别在于
symlink_status不会跟随符号链接。 - 调用前无需
exists() - 若出错会抛
filesystem_error。
fs::is_directory(p)/fs::is_regular_file(p)/fs::is_symlink(p)/is_block_file()/is_fifo()- 基于
status()返回值判断是否为目录、普通文件、符号链接、块设备(例如/dev/sda)和命名管道。分别对应下面的file_type枚举值file_type::regularfile_type::directoryfile_type::symlinkfile_type::blockfile_type::fifo
- 若
p不存在或权限不足会抛异常,推荐先exists()再判类型。
- 基于
fs::create_directory(p)- 创建单级目录。
- 返回创建成功返回
true,已存在则false - 失败抛异常。
- 不会递归
- 父目录缺失时也会抛异常。
fs::create_directories(p)- 递归创建所有缺失的目录层级。
- 返回如有新目录则
true,否则false - 失败抛异常。
- 避免先
exists()检查 - 有带
std::error_code&重载可免异常。
fs::remove(p)- 删除单个文件或空目录。
- 返回删除成功返回
true,否则false(如文件不存在) - 失败抛异常。
- 不递归;非空目录用
remove_all()。
fs::remove_all(p)- 递归删除路径及其所有内容。
- 返回删除的条目总数(含自身)
- 失败抛异常。
- 风险较高,调用前请确保路径正确
- 支持带
error_code重载。
fs::copy(src, dst, opts)- 复制文件或目录
- 无返回值
options可以取fs::copy_options::recursive:递归复制目录。update_existing:只覆盖目标中已有的条目且源更新时才替换。skip_existing、overwrite_existing、copy_symlinks等。
- 失败抛异常。
- 默认不递归,若复制目录必须加
fs::copy_options::recursive - 对目录结构复杂时留意符号链接选项。
fs::copy_file(src, dst, opts)- 仅复制单个文件。
- 返回
true(复制)或false(跳过,如目标不存在或相同时间戳且加了update_existing) - 失败抛异常。
- 相比
copy()更精细,推荐文件级操作时使用。
fs::rename(old_p, new_p)- 原子化地移动或重命名文件/目录。等同于 POSIX
rename()。 - 无返回值
- 失败抛异常。
- 跨设备会失败,需改用
copy()+remove();目标存在时覆盖行为依平台而异。
- 原子化地移动或重命名文件/目录。等同于 POSIX
fs::file_size(p)- 返回普通文件大小(字节)。
- 返回
uintmax_t,失败抛异常(非文件或不存在)。 - 对大文件很快
- 若需安全接口,可用带
error_code的重载。
fs::last_write_time(p)- 获取最后修改时间。
- 返回
file_time_type,可转换成std::chrono时间点 - 失败抛异常。
- 可与
system_clock或steady_clock互转,判断文件新旧。
fs::current_path()/fs::current_path(p)- 获取或设置当前工作目录。
- 返回获取时返回
path - 设置失败抛异常。
- 设置前确保目录存在;影响进程内所有相对路径解析。
fs::absolute(p)/fs::canonical(p)- 将路径转换为绝对形式。
absolute:不解析符号链接;canonical:解析所有符号链接,去除“..”、“.”。
- 返回转换后的绝对路径
- 失败抛异常。
canonical比absolute更“真实”,但更耗时。
- 将路径转换为绝对形式。
fs::relative(p, base)- 计算从
base到p的相对路径 - 如果
p无法由base表达,则返回未经修改的p。 - 返回值:一个
path,表示相对路径。 - 两个路径最好都使用同一风格(绝对或相对)并归一化(
canonical或absolute)后调用,以避免不可预期的“原样返回” - 若
base并非p的父层级,结果路径会包含“..”以回退; - 不抛异常,但若路径权限有问题,可能在内部调用
canonical时抛filesystem_error。
- 计算从
fs::permissions(p, perms, opt)- 修改目标的权限位。
- 参数:
perms是欲设置/增加/移除的权限掩码,opt指明操作模式(add、remove、replace)。 - Windows/Unix 表现略有差异;请先查询当前权限再修改。
fs::space(p)- 获取路径所在文件系统的容量信息。
- 返回
space_info{ capacity, free, available } - 失败抛异常。
- 区分
free(总空闲)和available(给当前用户的空闲)。
fs::equivalent(p1, p2)- 判断两个路径是否指向同一个文件或目录。
- 返回
true/false - 失败抛异常。
- 可用来检查硬链接或跨路径比较。
fs::create_symlink(target, link)/fs::create_directory_symlink(target, link)/fs::create_hard_link(target, link)- 分别创建符号链接(文件或目录)或硬链接。
- 无返回值
- 权限不足或目标不存在时抛异常。
fs::read_symlink(p)- 读取符号链接的目标路径(不跟随)。
- 返回指向目标的
path - 失败抛异常。
- 若
p不是符号链接会抛异常,可先is_symlink()再读。
fs::directory_iterator(dir)/fs::recursive_directory_iterator(dir)- 迭代遍历目录项。
- 非递归:仅遍历第一层;
- 递归:深入所有子目录。
recursive_directory_iterator支持disable_recursion_pending()跳过特定子目录- 遇到权限问题可设置
options::skip_permission_denied。
- 迭代遍历目录项。
std::filesystem::path类
以下是基于你给出的结构,使用统一格式,详细介绍 std::filesystem::path 类的常见操作,包括构造、拼接、分解、遍历、比较等功能:
类的构造和成员函数
path p1("dir/file.txt")/path p2 = u8"目录/文件.txt"- 使用字符串、
std::string,std::wstring, UTF-8 字面量等构造路径对象。 - 跨平台封装路径表示,自动处理分隔符(如 Windows 用
\,Unix 用/)。 - 构造仅仅是对字符串的封装,不会检查路径是否存在。
- 使用字符串、
p.string(),p.u8string()- 将路径对象转换为
std::string或 UTF-8 编码的字符串。
- 将路径对象转换为
路径的拼接
p1 / p2- 拼接路径,自动加上或去掉分隔符。例如:
path("dir") / "file.txt"→"dir/file.txt" - 右侧参数若为绝对路径则会“替换”左侧路径
- 拼接路径,自动加上或去掉分隔符。例如:
p.append("subdir")- 与
/类似,但是是path类的成员函数。 - 等价于
p /= "subdir",使用场景偏向链式操作。
- 与
p.concat("file.txt")- 直接字符串拼接,不添加路径分隔符。
path("dir/").concat("file.txt")→"dir/file.txt"- 关键点:用于修改文件名或扩展名部分,不用于路径层级拼接。
路径的分解
p.parent_path()- 获取父目录路径。例如
path("a/b/c.txt").parent_path()→"a/b" - 若路径为根目录或文件名,则返回空路径。
- 获取父目录路径。例如
p.filename()- 获取最后一级(文件名或最后目录名)。例如
path("a/b/c.txt").filename()→"c.txt" - 路径以分隔符结尾时,返回空路径。
- 获取最后一级(文件名或最后目录名)。例如
p.extension()- 获取文件扩展名(含“.”)。例如
path("c.txt").extension()→".txt" - 若文件名无扩展名,返回空路径。
- 获取文件扩展名(含“.”)。例如
p.stem()- 去除扩展名后的主文件名。例如
"c.txt"→"c" - 可结合
stem()+extension()修改或重构文件名。
- 去除扩展名后的主文件名。例如
路径的遍历
for (auto& part : path("a/b/c.txt"))- 遍历路径的每一级组件(如
a,b,c.txt)。 - 根目录和空路径也会被视为组件;
- 可以用
*begin()判断路径是否以/开头。
- 遍历路径的每一级组件(如
path::iterator是双向迭代器。
示例:
1
2
3
4
5
6
7
8
9path p("a/b/c.txt");
for (auto it = p.begin(); it != p.end(); ++it)
std::cout << *it << '\n';
/*
输出:
"a"
"b"
"c.txt"
*/用于逐层分析路径结构
路径的比较
==,!=- 比较路径字符串是否完全相等(字节级别,不解析符号链接)。
- 对路径的格式敏感,
"a/b"与"a//b"不等。
<,>,<=,>=- 按字典序比较路径,常用于排序或存入有序容器(如
std::set<path>)。 - 不是基于路径深度或文件存在与否,仅按字面比较。
- 按字典序比较路径,常用于排序或存入有序容器(如
p.lexically_normal()- 返回语义上等价但简化的路径(处理“.”、“..”、“重复分隔符”等)。例如
"a/./b/../c"→"a/c" - 不访问文件系统,仅字符串逻辑变换
- 用于规范化路径前的预处理。
- 返回语义上等价但简化的路径(处理“.”、“..”、“重复分隔符”等)。例如
常用操作
路径处理
1 | |
获取文件状态与元数据
1 | |
目录遍历
1 | |