文件系统 - 文件路径以及文件的状态和属性

文件系统 - 文件路径以及文件的状态和属性
苏丙榅1. 命名空间与路径
在 C++17 之前,C++ 程序员处理文件和目录是一件非常痛苦的事情。
- 在
Windows上,我们要用CreateFile,FindFirstFile,...等乱七八糟的 API。 - 在
Linux/macOS上,我们要用 POSIX 的open,stat,opendir,readdir等系统调用。 - 为了跨平台,我们可能还得依赖
Boost.Filesystem这样庞大的第三方库。
好消息是 C++17 终于把文件系统操作标准化了!它被称为 std::filesystem(一般缩写为 fs)。它不仅能跨平台,而且类型安全、接口现代、异常清晰。
std::filesystem 位于头文件 <filesystem> 中。为了偷懒,我们通常定义一个别名 fs。
1 |
|
C++17 引入的 std::filesystem 库(简称 fs)是现代 C++ 处理文件操作的重大里程碑。而在该库中,std::filesystem::path 类是最为核心的组件,用于通用地表示路径、解析路径字符串以及在不同操作系统间进行格式转换。它的核心设计理念是:
- 平台无关性:无论在 Windows (使用
"\\"或"/") 还是 POSIX (使用"/") 系统上,代码逻辑应保持一致。 - 存储类型:它内部使用类似于
std::basic_string的结构保存路径。在 Windows 上,原生字符类型是wchar_t;在 Linux/macOS 上是char。 - 视图模式:路径由通用的格式偏好和原生格式组成。
C++17 新特性<文件系统>在线 API 查询注意:
fs::path对象仅代表路径字符串,并不保证路径对应的文件或目录实际存在。要检查文件是否存在,需使用std::filesystem::exists(p)。
2. 路径的使用
2.1 构造与赋值
我们可以使用各种字符串类型来构造 path:
1 |
|
推荐写法:在跨平台代码中,尽量使用正斜杠 /。C++ 文件系统库会自动将其转换为操作系统的首选分隔符(例如在 Windows 上转换为 \)。
2.2 路径连接与修改
路径连接与修改是 path 最常用的功能之一。C++17 重载了 operator/ 和 operator/=,用于拼接路径。它会自动处理路径分隔符,避免出现双斜杠或缺失斜杠的问题。
代码中建议统一使用 /,std::filesystem 会自动将其转换为操作系统所需的分隔符(Windows 下为 \,Linux 下为 /)。
1 |
|
在 C++17 的 std::filesystem 中,使用 / 运算符拼接路径时,如果右操作数(dir1)是绝对路径(以根目录 / 开头),它会替换/丢弃左操作数(base1)之前的内容,而不是简单的字符串拼接。
- 第一步
base1 / dir1:由于dir1是/local,它会覆盖base1。结果变为/local。 - 第二步
(结果) / file1:拼接data.txt。结果变为/local/data.txt。
p.lexically_normal() 是 std::filesystem::path 类的一个成员函数。它的作用是生成一个新的路径对象,该对象代表了当前路径的规范形式。这个函数仅通过字符串分析(词法分析)来消除路径中的冗余部分,它不会访问硬盘(即不检查文件或目录是否真实存在)。
2.3 路径分解
path 提供了成员函数来将路径拆解为各个组成部分。这些函数返回新的 path 对象。
| 函数 | 描述 | 示例路径: /foo/bar.txt |
|---|---|---|
root_name() |
根名称(如驱动器盘符、网络位置) | Linux : 空,Win 下为 "C:" |
root_directory() |
根目录 | / |
root_path() |
根名称 + 根目录 | / (Win 下为 "C:/") |
relative_path() |
相对路径部分(去掉 root_path) | foo/bar.txt |
parent_path() |
父目录路径 | /foo |
filename() |
文件名或目录名 | bar.txt |
stem() |
主文件名(不含扩展名) | bar |
extension() |
扩展名(含点) | .txt |
代码示例:
1 | fs::path p = "/tmp/project/source.cpp"; |
注意 stem 的特殊情况:
- 文件
."(只有点) -> stem 是. - 文件
..(双点) -> stem 是.. - 文件
.profile-> stem 是.profile(没有扩展名)
2.4 路径查询与状态
在 C++17 的 std::filesystem::path 类中,有一系列函数用于检查路径自身的语法属性(即检查字符串本身的特征,而不是检查磁盘上文件的实际状态)。
检查路径是否为空
函数原型 返回值 说明 bool empty() const noexceptbool检查路径是否为空(即路径字符串的长度是否为 0)。 检查特定路径组件是否存在
这些函数检查路径中是否包含根目录、相对路径或文件名。
函数原型 返回值 说明 bool has_root_path() constbool检查是否有根路径(包含根名称和根目录,如 Windows 的 C:或 Unix 的/)。bool has_root_name() constbool检查是否有根名称(如 Windows 的 C:或网络路径\\server)。bool has_root_directory() constbool检查是否有根目录(如 Unix 的 /)。bool has_relative_path() constbool检查是否有相对路径部分(即除去根路径后的剩余部分)。 bool has_parent_path() constbool检查是否有父路径(即路径的上一级目录是否存在)。 bool has_filename() constbool检查是否有文件名(通常指路径的最后一个组件)。 bool has_stem() constbool检查是否有主文件名(即不包含扩展名的文件名)。 bool has_extension() constbool检查是否有文件扩展名(如 .txt)。检查路径的性质(绝对 vs 相对)
函数原型 返回值 说明 bool is_absolute() constbool检查是否为绝对路径(Unix 下以 /开头,Windows 下以盘符开头或开头有/)。bool is_relative() constbool检查是否为相对路径(非绝对路径)。 返回迭代器相关的函数
虽然这些函数不直接返回布尔值,但它们提供了遍历路径组件的能力,常被用于精细的路径分析或迭代器检查。
函数原型 返回值类型 说明 begin() constiterator返回指向路径第一个组件的迭代器。 end() constiterator返回指向路径末尾的迭代器。
代码示例:
1 |
|
上述列表中的 has_xxx 函数仅检查路径字符串的语法结构。如果你需要检查磁盘上文件的实际属性(例如:文件是否真的存在、是否为文件夹、是否隐藏等),需要使用 std::filesystem::directory_entry 或 std::filesystem::status 相关的函数(如 exists(), is_directory(), is_regular_file())。
3. 文件状态和属性
在 C++17 std::filesystem (文件系统库) 中,查看文件状态的核心机制是通过 std::filesystem::file_status 类以及相关的辅助函数来实现的。
这套机制允许程序在不打开文件的情况下,获取文件的元数据,例如:文件是否存在、是普通文件还是目录、文件大小、权限等。
3.1 核心类 file_status
file_status 是一个轻量级的类,用于存储文件的类型和权限。
- 主要功能:封装了文件类型(
file_type)和文件权限(perms)。 - 获取方式:通常不直接构造,而是通过
status()或symlink_status()函数获取。
文件类型std::filesystem::file_type 描述了文件系统的对象类型。常见的值包括:
| 枚举值 | 含义 | 说明 |
|---|---|---|
none |
未知类型 | 状态尚未确定或发生错误。 |
not_found |
文件未找到 | 路径不存在。 |
regular |
普通文件 | 常规的磁盘文件(如 .txt, .exe)。 |
directory |
目录 | 文件夹。 |
symlink |
符号链接 | 软链接(快捷方式)。 |
block |
块设备 | 块特殊文件(如硬盘分区)。 |
character |
字符设备 | 字符特殊文件(如键盘、终端)。 |
fifo |
管道 | 命名管道 (IPC)。 |
socket |
套接字 | Unix 域套接字。 |
unknown |
类型未知 | 文件存在但类型无法识别。 |
文件权限std::filesystem::perms 通常对应 POSIX 系统的权限位。主要值包括(通过位掩码组合使用):
none: 无权限。owner_read,owner_write,owner_exec: 文件所有者的读、写、执行权限。group_read,group_write,group_exec: 文件所属组的读、写、执行权限。others_read,others_write,others_exec: 其他用户的读、写、执行权限。all: 所有权限(等同于0777)。set_uid,set_gid,sticky_bit: 特殊位(设置用户ID、设置组ID、粘滞位)。mask,unknown: 掩码和未知标志。
除了直接获取 file_status 对象并检查其类型,C++17 还提供了一组极为便捷的辅助函数。这些函数内部会自动调用 status(),因此你无需手动处理中间对象(这些is_xxx函数都是重载函数,最常用的就是给他们的参数传递一个文件路径path)。
| 文件类型 | 函数功能 | 返回值 |
|---|---|---|
| is_block_file | 检查给定的路径是否表示块设备 | bool |
| is_character_file | 检查给定的路径是否表示字符设备 | bool |
| is_directory | 检查给定的路径是否表示一个目录 | bool |
| is_empty | 检查给定的路径是否表示一个空文件或空目录 | bool |
| is_fifo | 检查给定的路径是否表示一个命名管道 | bool |
| is_other | 检查参数是否表示一个其他文件 (非文件、非目录、非链接) | bool |
| is_regular_file | 检查参数是否表示一个常规文件 | bool |
| is_socket | 检查参数是否表示一个具名 IPC 套接字 | bool |
| is_symlink | 检查参数是否表示一个符号链接 | bool |
| status_known | 检查文件状态是否已知(参数为std::filesystem::file_status类型) |
bool |
上面说完了类型和权限,std::filesystem 还提供了获取其他元数据的函数,它们通常直接作用于 path 或 directory_entry。
| 函数名 | 作用 | 返回类型 |
|---|---|---|
file_size(path) |
获取文件大小(字节) | uintmax_t |
hard_link_count(path) |
获取硬链接数 | uintmax_t |
last_write_time(path) |
获取最后修改时间 | file_time_type |
3.2 查询状态的函数
在 C++17 中,获取文件状态的功能主要通过头文件 <filesystem>(即 std::filesystem 库)提供。有三个主要的函数用于获取文件状态,它们分别处理不同的场景和符号链接的行为。
这三个核心函数是:
std::filesystem::statusstd::filesystem::symlink_statusstd::filesystem::status_known
3.2.1 std::filesystem::status
这是最常用的获取文件状态的函数。它的主要作用是获取文件路径所指向的文件系统对象的状态信息(如类型、权限等)。函数原型为:
1 | std::filesystem::file_status status(const std::filesystem::path& p); |
关键行为(符号链接处理):
- 跟随符号链接:如果路径
p指向的是一个符号链接,status函数会自动解引用。也就是说,它会返回符号链接所指向的实际目标文件的状态,而不是符号链接本身的状态。 - 例如:如果
link.txt->target.txt,调用status("link.txt")将返回target.txt的属性(如是否为常规文件、大小等)。
- 跟随符号链接:如果路径
返回值:
- 返回一个
std::filesystem::file_status对象。 - 该对象可以通过
.type()获取文件类型(如regular_file,directory,block_file等)。 - 该对象可以通过
.permissions()获取文件权限。
- 返回一个
异常与错误处理:
- 如果文件不存在(或无法访问),默认会抛出
std::filesystem::filesystem_error异常。 - 使用带有
std::error_code& ec参数的重载版本,不会抛出异常。如果出错,会将ec设置为相应的错误码,并且返回的file_status对象的类型为file_type::not_found或file_type::unknown。
- 如果文件不存在(或无法访问),默认会抛出
返回值
file_status类的成员函数:- 这些函数用于获取或修改
file_status对象中存储的状态信息。std::filesystem::file_type type() const noexcept:- 获取文件类型。
- 返回对象当前存储的文件类型(
std::filesystem::file_type枚举值)。
void type(std::filesystem::file_type ft) noexcept:- 设置文件类型。
- 将对象的文件类型设置为
ft。
std::filesystem::perms permissions() const noexcept:- 获取文件权限。
- 返回对象当前存储的文件权限(
std::filesystem::perms枚举值,通常是位掩码)。
void permissions(std::filesystem::perms prms) noexcept:- 设置文件权限。
- 将对象的文件权限设置为
prms。
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace fs = std::filesystem;
int main()
{
// 假设我们有一个文件路径
fs::path p = "logfile.txt";
// 获取文件状态
fs::file_status status = fs::status(p);
// 使用成员函数检查类型和权限
std::cout << "File type: ";
switch (status.type())
{
case fs::file_type::regular: std::cout << "Regular file\n"; break;
case fs::file_type::directory: std::cout << "Directory\n"; break;
case fs::file_type::not_found: std::cout << "Not found\n"; break;
default: std::cout << "Other\n"; break;
}
// 检查是否有读权限
if ((status.permissions() & fs::perms::owner_read) != fs::perms::none)
{
std::cout << "Owner has read permission." << std::endl;
}
return 0;
}- 这些函数用于获取或修改
3.2.1 std::filesystem::symlink_status
std::filesystem::symlink_status 是 C++17 <filesystem> 库中的一个自由函数(非成员函数),与 std::filesystem::status 函数非常相似,但它们之间存在一个关键的区别:对符号链接的处理方式。
symlink_status 是一个重载函数,通常有两种形式:
形式1:接受路径
1
2std::filesystem::file_status symlink_status( const std::filesystem::path& p );
std::filesystem::file_status symlink_status( const std::filesystem::path& p, std::error_code& ec ) noexcept;参数:
p: 要检查的文件系统路径(可以是符号链接)。ec: (可选) 错误码。如果提供此参数,函数在出错时不会抛出异常,而是将错误码写入ec。如果提供的路径
p不存在,或者发生权限错误:- 不带
error_code的版本: 抛出std::filesystem::filesystem_error异常。 - 带
error_code的版本: 不抛出异常。- 将
ec设置为相应的错误代码。 - 返回一个特殊的
file_status对象,其type()为file_type::none。
- 将
- 不带
返回值: 一个
std::filesystem::file_status对象。
形式2:接受目录
1
2std::filesystem::file_status symlink_status( const std::filesystem::directory_entry& d );
std::filesystem::file_status symlink_status( const std::filesystem::directory_entry& d, std::error_code& ec ) noexcept;directory_entry类本身缓存了状态信息,调用此方法会获取或刷新该目录项的符号链接状态。
下面是 symlink_status 的示例代码:
1 |
|
3.2.2 std::filesystem::status_known
std::filesystem::status_known(st) 是 C++17 引入的标准库 <filesystem> 中的一个辅助函数。它用于检查给定的 file_status 对象是否包含已知的文件属性信息。函数原型如下:
1 | namespace std::filesystem |
参数:
s:一个std::filesystem::file_status类型的对象,通常由status()、symlink_status()或其他相关函数返回。
返回值:
- 如果
s包含已知的文件信息,则返回true。 - 如果
s不包含任何已知信息,则返回false。
下面的示例展示了不同情况下 status_known 的返回值:
1 |
|
根据 C++ 标准(C++17 及后续版本),std::filesystem::status_known 的唯一判断标准是:file_status 对象里的类型是否为 file_type::none。这就意味着:
- 如果文件类型是
regular_file,directory,symlink… -> 返回true - 如果文件类型是
not_found-> 返回true - 如果文件类型是
unknown-> 返回true - 只有当文件类型是
none(默认空状态) -> 返回false



















