C++ 判断文件是否存在

在读取配置文件或处理用户数据之前,我们通常需要先确认磁盘上的目标文件到底存不存在,避免直接打开不存在的路径导致读取失败。

在 <filesystem> 标准库中,fs::path 只负责在内存里保存路径名称。如果想要去磁盘上检查某个路径是否存在、区分它是普通文件还是文件夹,我们可以调用 fs::exists()、fs::is_regular_file() 和 fs::is_directory() 等函数。

使用 exists() 判断路径是否存在

在 C++ 中,fs::exists() 函数的作用非常直观,就是去磁盘上检查指定路径当前是否存在。就像我们在纸上写了一个房间地址,并不代表那间房子真实存在,只有调用 fs::exists() 才会真正去文件系统里查一查,如下图所示。

fs::exists() 检查磁盘路径是否存在的工作机制

语法:

fs::exists(path)

说明:

这里的 fs 是命名空间 std::filesystem 的常用别名。fs::exists() 接收一个 fs::path 对象或路径字符串作为参数,并返回一个 bool 值:如果该路径在磁盘上存在,返回 true;如果不存在,则返回 false。

示例 1:判断路径是否存在

#include <filesystem>
#include <fstream>
#include <iostream>

namespace fs = std::filesystem;

int main() {
    // 先创建一个测试文件 lvye.txt
    std::ofstream outFile("lvye.txt");
    outFile << "lvyenet\n";
    outFile.close();

    fs::path p1 = "lvye.txt";
    fs::path p2 = "missing.txt";

    bool e1 = fs::exists(p1);
    bool e2 = fs::exists(p2);

    std::cout << std::boolalpha;    // 让 bool 值显示为 true 或 false
    std::cout << "lvye.txt 是否存在:" << e1 << '\n';
    std::cout << "missing.txt 是否存在:" << e2 << '\n';
    return 0;
}

运行结果如下。

lvye.txt 是否存在:true
missing.txt 是否存在:false

分析:

在这个例子中,我们先创建了测试文件 "lvye.txt",所以调用 fs::exists(p1) 检查时返回 true。而当前目录下并没有 "missing.txt",因此 fs::exists(p2) 返回 false。

另外,代码中的 std::boolalpha 是输出格式控制符,作用是让后面的 bool 值直接打印为 true 或 false,而不是默认的数字 1 或 0。

判断文件类型与获取大小

需要特别注意的是,fs::exists() 返回 true 只能说明 “这个路径上有东西存在”,但它不会区分普通文件与文件夹。比如磁盘上刚好有一个名叫 config 的文件夹,fs::exists("config") 也会返回 true,这时直接拿文件流当文本去读就会失败。

因此在实际开发中,我们经常使用 fs::is_regular_file() 判断目标是不是普通文件,使用 fs::is_directory() 判断是不是目录,确认是普通文件后再调用 fs::file_size() 查看字节数。这几个常用函数的对比如下表所示。

路径判断与大小函数
函数 作用说明 返回值说明
fs::exists(path) 判断路径是否存在 文件或目录存在时均返回 true
fs::is_regular_file(path) 判断是否为普通文件 路径存在且为普通文件时返回 true
fs::is_directory(path) 判断是否为目录 路径存在且为目录时返回 true
fs::file_size(path) 获取普通文件的大小 返回字节数,类型为 std::uintmax_t

语法:

fs::is_regular_file(path)
fs::is_directory(path)
fs::file_size(path)

说明:

其中,regular 是英文 “常规、普通” 的意思,用来区分文件夹或系统设备等特殊条目。当我们调用 fs::is_regular_file(path) 或 fs::is_directory(path) 时,如果路径本身不存在,它们会直接返回 false 而不会报错。

也就是说,如果我们只需要确认 “它是不是已存在的普通文件”,直接调用 fs::is_regular_file(path) 一句代码就够了,不需要在前面多写一次 fs::exists(path)。

至于 fs::file_size(path) 返回的 std::uintmax_t,是 <cstdint> 头文件中定义的无符号大整数类型,专门用来表示大文件的字节大小。日常打印或比较大小时,大家直接把它当成普通的非负整数使用即可。

示例 2:判断类型与查看大小

#include <cstdint>         // 提供 std::uintmax_t
#include <filesystem>
#include <fstream>
#include <iostream>

namespace fs = std::filesystem;

int main() {
    // 写入 7 个字母,占 7 个字节
    std::ofstream outFile("lvye.txt");
    outFile << "lvyenet";
    outFile.close();

    fs::path file = "lvye.txt";
    fs::path dir = ".";    // "." 表示当前工作目录

    bool isFile = fs::is_regular_file(file);
    bool isDir = fs::is_directory(file);
    std::uintmax_t size = fs::file_size(file);
    bool cwdIsDir = fs::is_directory(dir);

    std::cout << std::boolalpha;
    std::cout << "lvye.txt 是普通文件:" << isFile << '\n';
    std::cout << "lvye.txt 是目录:" << isDir << '\n';
    std::cout << "lvye.txt 的大小:" << size << " 字节\n";
    std::cout << "当前路径 . 是目录:" << cwdIsDir << '\n';
    return 0;
}

运行结果如下。

lvye.txt 是普通文件:true
lvye.txt 是目录:false
lvye.txt 的大小:7 字节
当前路径 . 是目录:true

分析:

由于 lvye.txt 是普通文本文件,isFile 的结果为 true,isDir 的结果为 false。我们在文件里写入了 "lvyenet" 共 7 个英文字母,每个英文字母占 1 个字节,所以 fs::file_size(file) 返回的文件大小为 7。

同时,代码中的 "." 代表当前工作目录。它本身是一个文件夹,因此 fs::is_directory(dir) 返回 true。

注意 fs::file_size() 只能用来查询普通文件的大小。小伙伴们千万不要对文件夹或不存在的路径直接调用该函数,否则程序运行时会抛出异常报错。在不确定路径类型时,建议先通过 fs::is_regular_file() 确认后再查询大小。

使用 error_code 免异常检查

在前面的例子中,我们调用 fs::exists(path) 时只传入了一个路径参数,大多数情况下这样写都没问题。但如果遇到没有访问权限的系统目录或磁盘故障,单参数版本就会直接抛出异常,需要配合 try...catch 来捕获。

如果我们不希望程序抛出异常,并且想把 “路径单纯不存在” 和 “查询过程本身出错” 这两种情况区分开,可以在调用函数时额外传入第 2 个参数 std::error_code。

语法:

std::error_code ec;
fs::exists(path, ec)

说明:

在使用 std::error_code 之前,我们需要先通过 #include <system_error> 引入头文件。ec 是专门用来接收错误信息的错误码对象,判断规则如下:

  • 查询过程出错:程序不会抛出异常,而是把非 0 的错误编号记录到 ec 中。此时 if (ec) 条件为 true,我们可以调用 ec.message() 查看具体的错误描述。
  • 查询过程正常:错误码保持为 0(即无错误),此时 if (ec) 条件为 false,接着我们再根据函数返回值是 true 还是 false 判断路径是否存在。

示例 3:结合 error_code 检查路径

#include <filesystem>
#include <iostream>
#include <system_error>

namespace fs = std::filesystem;

int main() {
    fs::path path = "no_such_entry.txt";
    std::error_code ec;

    bool exists = fs::exists(path, ec);
    if (ec) {
        std::cout << "查询出错:" << ec.message() << '\n';
        return 1;
    }

    if (exists) {
        std::cout << "路径存在\n";
    } else {
        std::cout << "路径不存在,且查询过程未出错\n";
    }

    return 0;
}

运行结果如下。

路径不存在,且查询过程未出错

分析:

在这个例子中,虽然当前目录下并没有 "no_such_entry.txt",但操作系统顺利执行完了这次查找,中途没有遇到权限拦截或磁盘故障。因此 ec 保持无错误状态,if (ec) 判断为 false,同时返回值 exists 为 false。

这一点小伙伴们要分清楚:“文件不存在” 本身是一个正常的查询结果,并不属于系统故障。除了 fs::exists(),前面介绍的 fs::is_regular_file()、fs::is_directory() 和 fs::file_size() 也都支持在末尾传入 ec 参数。

上一篇:

下一篇:

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号