在类Unix系统上,用access(path, R_OK)最直接判断读权限,返回0表示有权限,-1需结合errno区分原因;Windows则应调用CreateFileW()尝试以GENERIC_READ打开并检查错误码,而非依赖GetFileAttributes()。

用 access() 判断读取权限最直接
在类 Unix 系统(Linux/macOS)上,access() 是标准且轻量的方式。它不打开文件,只检查当前进程对路径的权限,避免了竞态和资源占用。
关键点:传入 R_OK 作为第二个参数,返回 0 表示有读权限,-1 表示无权限(需结合 errno 判断原因)。
-
access()检查的是**真实用户 ID(real UID)** 的权限,不是有效 UID(e.g. setuid 程序中要注意) - 路径必须存在且是常规文件或目录;若路径不存在或为 broken symlink,
access()返回 -1 且errno == ENOENT - 对目录传
R_OK表示“能否列出目录内容”,不是“能否读取目录内文件”——这是常见误解
#include <unistd.h>
#include <errno.h>
<p>bool can_read(const char* path) {
if (access(path, R_OK) == 0) return true;
// 区分是没权限,还是路径根本不存在/不可达
if (errno == EACCES) return false; // 明确拒绝
return false; // 其他错误(如 ENOENT、ENOTDIR)也视为不可读
}Windows 下要用 GetFileAttributes() + GENERIC_READ 检查
Windows 没有直接对应 access() 的 API,GetFileAttributes() 只能粗略判断是否为只读文件(FILE_ATTRIBUTE_READONLY),但该属性不等价于“无读权限”——NTFS 权限才是关键。
可靠做法是尝试以只读方式打开:调用 CreateFile() 并指定 GENERIC_READ 和 FILE_SHARE_READ,检查返回句柄是否为 INVALID_HANDLE_VALUE。
立即学习“C++免费学习笔记(深入)”;
- 必须用
CREATE_NO_WINDOW和NULL安全描述符,避免弹窗或权限提升风险 - 注意关闭句柄(
CloseHandle()),否则泄漏 HANDLE -
ERROR_ACCESS_DENIED表示无读权限;ERROR_FILE_NOT_FOUND或ERROR_PATH_NOT_FOUND表示路径无效
#include <windows.h>
<p>bool can_read_win(const wchar_t* path) {
HANDLE h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
return (err == ERROR_ACCESS_DENIED) ? false : false;
}
CloseHandle(h);
return true;
}跨平台封装要注意权限语义差异
Unix 的 R_OK 和 Windows 的 GENERIC_READ 表面相似,实际行为不同:前者检查 DAC(discretionary access control)位,后者触发完整的 ACL(access control list)评估,包括继承权限、组策略等。
这意味着同一路径在两个系统上可能返回不同结果,尤其在域环境或启用了 UAC 的 Windows 上。
- 不要缓存检查结果——权限可能被其他进程动态修改(如 chmod / icacls)
- 如果后续操作是打开文件读取,**不如直接尝试打开并处理失败**,避免 TOCTOU(time-of-check to time-of-use)竞态
- C++20 的
std::filesystem::status()只能获取文件类型和基础属性(如是否为 directory),不提供权限检查能力,别误用
权限检查 ≠ 实际可读,真正可靠的只有打开后读取
即使 access() 或 CreateFile() 成功,也不能保证后续 fopen() 或 std::ifstream 一定成功:文件可能被瞬间删除、挂载点卸载、磁盘满、SELinux/AppArmor 拦截、甚至 NFS 服务器宕机。
所以生产代码里,权限检查更适合用于 UI 灰化按钮或提前报错提示,而非替代真正的 I/O 错误处理。
最容易被忽略的一点:路径字符串本身编码问题。Linux 用 UTF-8 路径名没问题,但 Windows 的 CreateFileW() 要求 wchar_t*,若传入窄字符路径且含非 ASCII 字符,会直接失败——别忘了做编码转换。


















