使用 boost::filesystem,如何防止覆盖以前的密码文件?

Using boost::filesystem, how can I prevent overwriting of previous password files?

好的,我对 C++ 和一般编码还很陌生。我正在研究一个密码生成器,它随机生成一个介于 33 和 126 之间的数字,然后使用相应的 ASCII 代码并创建一个用户指定长度的密码。然后我想使用 boost::filesystem 创建一个密码文件夹,并在该文件夹中使用 std::ofstream 创建一个包含密码的文本文件。我已经弄清楚如何创建文件夹,以及如何创建文件。但是......我需要一种方法来确定文件夹中是否已经存在密码文件,如果是,则按数字顺序创建不同名称的密码文件(因为写入文件会覆盖以前的密码)。例如,如果我要 运行 程序三次,在密码文件夹中将有三个文件,分别命名为 'password.txt'、'password2.txt' 和 'password3.txt'。这是我目前所拥有的:

namespace fs = boost::filesystem;

void savePassword(char array[], fs::path dir)
{
    std::cout << "Saving...\n";
    //this is where there needs to be a check, and the check needs to return how many password files there are
    std::ofstream savePw;
    savePw.open("./Passwords/password.txt"); //check to see if file exists before this, if so, make it "./Passwords/password" += number of files += ".txt"
    if (!savePw) {
        std::cout << "ERROR: File could not be opened.\n";
    }
    else {
        savePw << "Your password is:\n" << static_cast<std::string>(array) << '\n';
        savePw.close();
        std::cout << "Saved to: " << dir << '\n' << "Thanks for using the SPG!\n";
    }
}

fs::path createFolder() //creates folder and returns the directory to be passed to savePassword()
{
    fs::path dir{ boost::dll::program_location().parent_path() += "\Passwords" };
    fs::create_directory(dir);
    return dir;
}

做了很多研究,但还没有真正发现任何东西。我试过 boost::filesystem::directory_iterator,但不太明白如何使用它。我也试过将路径转换为字符串并使用 std::stoi,但失败了。任何帮助深表感谢。 :)

Boost.Filesystem 提供一个名为 exists, along with other operations for querying status 的文件操作。您可以使用如下循环生成文件名:

fs::path generate_filename(fs::path const& dir)
{
    fs::path path = dir / "password.txt";
    unsigned int counter = 1u;
    while (true)
    {
        if (!fs::exists(path))
            break;

        ++counter;
        if (counter == 0u)
            throw std::runtime_error("Failed to generate a unique file name");

        path = dir / std::string("password")
            .append(std::to_string(counter)).append(".txt");
    }

    return path;
}

但是,请注意该代码是活泼的,因为可以在调用 exists 和打开文件进行写入之间创建文件。解决方法是在打开文件的同时测试文件是否存在。

从 C++20 开始,标准 C++ 文件流不允许在打开文件进行写入时测试文件是否存在,因此您将不得不使用不同的文件 API。从 C11 开始,fopen 允许在文件打开模式中指定“x”以指示如果文件存在则该函数必须失败。您必须咨询您的编译器或标准 C 库文档,看看它是否支持这个新增功能。

或者,您可以使用低级文件系统 API,例如 open call with O_CREAT and O_EXCL flags on POSIX systems or CreateFile 和 Windows 上的 CREATE_NEW 标志。


几个不相关的笔记。

  1. 您不需要 static_cast<std::string>(array) 将 C-style 字符串输出到流。直接输出一个字符数组是可行的——数组中的字符将被写入流中,直到第一个空字符(终止符)。通过强制转换,您只是不必要地分配动态内存并在输出之前复制字符串。

  2. 避免在函数参数中按值接受潜在的大对象,如容器、字符串或路径,除非函数体需要。您的 fs::path dir 参数可以更好地被接受为常量引用 (fs::path const& dir),这可能会避免在调用 savePassword.

    时必须复制路径