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

C++ STL之filesystem文件系统库详解

C++ STL之filesystem文件系统库详解

一、从 Boost 到标准库:filesystem 的前世今生

C++17 之前,跨平台文件操作是噩梦。Windows 用GetFileAttributesW、Linux 用stat、Mac 用NSFileManager——每套 API 各有千秋,但无一适用于跨平台场景。多数项目转向boost::filesystem,但 Boost 毕竟不是标准,团队引入需额外依赖、配置构建链。

C++17 正式将std::filesystem纳入标准库,头文件<filesystem>,命名空间std::filesystem。它统一了路径分隔符(/vs\)、文件属性提取、目录遍历、磁盘容量查询等操作,且无第三方依赖。

#include<filesystem>namespacefs=std::filesystem;intmain(){fs::path p="/home/user/file.txt";boolexists=fs::exists(p);return0;}

二、path 类与正规化

path是 filesystem 库的核心类型,负责跨平台路径表示。它不检查路径是否真实存在,只做字符串操作。

常见操作

操作说明
p.root_name()返回根名(Windows 的C:
p.root_directory()返回根目录(/\
p.parent_path()父路径
p.filename()文件名(含后缀)
p.stem()不带后缀的文件名
p.extension()后缀(含.

正规化:lexically_normal

移除路径中的...和多余分隔符,返回逻辑上"干净"的路径。

fs::path p="a/./b/../c/file.txt";fs::path norm=p.lexically_normal();// 结果: "a/c/file.txt"

如果路径中有.表示当前目录,..表示上一级父目录,lexically_normal会将它们约简。注意这是纯字符串操作,不会访问磁盘。

make_preferred

将路径分隔符转为当前平台惯用格式:Windows 转\,POSIX 转/

fs::path p="a/b/c";p.make_preferred();// Windows: a\b\c, Linux: a/b/c

原始路径: a/./b/../c/file.txt

lexically_normal

去除 .

回退 ..

正规化结果: a/c/file.txt

make_preferred

Windows: a\\b\\c

POSIX: a/b/c

三、directory_entry 的缓存机制

directory_entry封装了文件系统的一条记录,包括路径和属性。它的核心设计是缓存:首次访问文件属性后,结果会被内部缓存,后续访问不再触发系统调用。

fs::directory_entryentry("/home/user/file.txt");autosize1=entry.file_size();// 触发 statautosize2=entry.file_size();// 返回缓存,无系统调用entry.refresh();// 强制刷新缓存autosize3=entry.file_size();// 重新 stat

这在遍历大量文件时能显著减少 I/O:一次directory_iterator操作自动填充directory_entry,后续取file_sizelast_write_timefile_type等都走缓存。

四、目录遍历:directory_iterator 与 recursive_directory_iterator

两套 API 对应两种遍历范围:

特性directory_iteratorrecursive_directory_iterator
遍历深度仅当前目录递归子目录
是否含...
排序未定义未定义
跳过子目录不适用disable_recursion_pending()
voidlist_all(constfs::path&dir){fs::directory_iterator end;for(autoit=fs::directory_iterator(dir);it!=end;++it){// 只有第一层}for(autoit=fs::recursive_directory_iterator(dir);it!=fs::recursive_directory_iterator();++it){// 递归全部if(it->is_directory()&&skip(*it))it.disable_recursion_pending();}}

开始遍历

选择迭代器类型

directory_iterator?

recursive_directory_iterator?

打开当前目录

读取下一个条目

还有条目?

返回 directory_entry

结束

打开当前目录

读取下一个条目

还有条目?

是目录且
未禁用递归?

递归进入子目录

返回 directory_entry

两种迭代器返回的都是directory_entry,可直接调用is_regular_file()file_size()path()等。

五、space_info:磁盘容量查询

std::filesystem::space()返回space_info结构体:

structspace_info{uintmax_t capacity;// 总容量uintmax_t free;// 剩余容量(给调用者的配额)uintmax_t available;// 可用容量(含非特权用户的配额限制)};
fs::space_info si=fs::space("/");std::cout<<"总容量: "<<si.capacity/1e9<<" GB\n";std::cout<<"剩余: "<<si.free/1e9<<" GB\n";std::cout<<"可用: "<<si.available/1e9<<" GB\n";

freeavailable的区别:POSIX 系统上,free是文件系统层未使用的块数,available还要扣除 root 预留块。非 root 用户看到available小于free

六、copy 与 copy_options

fs::copy复制文件或目录,搭配copy_options控制行为:

选项效果
none默认,已存在时报错
skip_existing跳过已有文件,不报错
overwrite_existing覆盖已有文件
update_existing仅目标更旧时才覆盖
recursive递归复制子目录
directories_only仅复制目录结构
create_symlinks复制为符号链接
voidbackup(constfs::path&src,constfs::path&dst){fs::copy_options opts=fs::copy_options::recursive|fs::copy_options::overwrite_existing|fs::copy_options::update_existing;fs::copy(src,dst,opts);}

注意fs::copy在目录上默认不递归,必须显式加copy_options::recursive才能复制子目录。

七、面试题精选

1.lexically_normal会访问磁盘吗?

不会。它是纯字符串操作,只处理./../路径分量,不检查文件是否存在。如果需要解析符号链接或相对路径的绝对化,用canonicalweakly_canonical(会访问磁盘)。

2.directory_iterator返回的条目有顺序保证吗?

没有。遍历顺序由底层文件系统决定,不可预测。若需有序遍历,须手动收集到std::vector再排序。

3.directory_entry缓存什么时间生效?如何刷新?

首次调用file_size()last_write_time()status()等触发系统调用后缓存结果。文件在外部被修改时缓存变脏,需调用refresh()刷新。缓存不自动感知外部变更。

4.copy_options::recursivecopy_options::directories_only可以同时使用吗?

可以。组合后会在目标递归创建完整的目录结构,但不复制任何文件。常用于备份前建好骨架。

5.space_info::freespace_info::available实际有什么区别?

free是文件系统层空闲块,available还要排除特权预留(ext4 默认 5% 给 root)。用户态程序应优先读available,否则写的字节可能被ENOSPC拒绝。

6.pathoperator/=做了什么?与字符串拼接有何不同?

operator/=会自动插入平台分隔符(/\),且不会重复。字符串拼接可能导致a//ba\b混用。优先用operator/=而不是fs::path(a.string() + "/" + b.string())

7.recursive_directory_iterator如何跳过某个子目录?

调用disable_recursion_pending(),析构后迭代器会跳过当前条目(须是目录)下的所有内容。

for(autoit=fs::recursive_directory_iterator(".");it!=fs::recursive_directory_iterator();++it){if(it->path().filename()==".git")it.disable_recursion_pending();elseprocess(*it);}

8. 如何判断一个path是绝对路径?

  • p.is_absolute():检查是否绝对路径(POSIX 以/开头,Windows 含根名+根目录)
  • p.is_relative():上述取反

Windows 上C:/foo是绝对路径,.是相对路径,/foo仅含根目录但无根名,按 Windows 规则也是相对路径。

http://www.jsqmd.com/news/1119448/

相关文章:

  • LeetCode 380 O (1) 时间插入、删除和获取随机元素
  • 202636读书笔记|《重走三毛之路:我们活在现在,不活在将来》——不被既有的规则所束缚,勇于突破
  • 我用了十年终端,今天才真正学会使用它
  • 复变函数:拉普拉斯变换---傅里叶变换的扩展
  • 【LE Audio】CSIP精讲[3]: 设备端协同集标识的核心实现与落地要点
  • 【皇榜科技线路板质量课堂·第30篇】散布图(Scatter Plot):压合温度与剥离强度的关系,看图说话
  • *表示媒体(Representation Media)**:指为了加工、处理和传输感觉媒体而人为构造的编码形式
  • 【毕业季福利】6000套计算机源码免费领!附带“免检级”初稿整包生成神器!
  • LLM 全解析:大语言模型原理、三种接入方案与 DeepSeek API 实战
  • C# try-catch 异常处理全套笔记
  • iOS Trace 分析入门到实战:符号化、Run 数据与卡顿归因
  • o3与o3-pro模型选型指南:成本、可靠性与长上下文实战
  • C++ STL 之 string_view 详解
  • 大模型科研写作能力盲测:Gemini 2.0作为裁判的五维评估实践
  • 企业智能体如何落地?从工作流编排、知识库调用到模型统一管理
  • 2007-2024年 供应链风险数据文本分析法 +文献
  • 音圈电机双闭环PID控制:提升精密定位性能的关键技术
  • 时序模型为何零样本胜出?
  • 最新AI论文写作工具综合榜(2026 优选)
  • OpenClaw 构建报错 FATAL ERROR: Reached heap limit - JavaScript heap out of memory 的解决方案
  • Lauterbach调试Cortex-R52架构多核芯片问题
  • QueryExcel终极指南:3分钟搞定100个Excel文件的批量查询神器
  • PT工具常用的debug指令(持续更新)
  • 【CMD】查找线程名称为Simulation的进程并把他杀掉
  • 黑金古刀-永劫助手(BlackGoldAncientSword)——《永劫无间》战绩查询与队友识别桌面工具
  • [C语言]Excel转换JsonObject
  • 《Java 100 天进阶之路》第50篇:阻塞队列与并发容器(2026版)
  • C4模型中的Level 1(System Context Diagram,系统上下文图)是C4模型最顶层的抽象视图
  • Code Combat | 极客战记 攻略【Kithgard地牢篇 14/42】祸之火焰
  • 模型训练后的第一件事?微调?NO