Нумерованные уникальные файлы - PullRequest
1 голос
/ 01 мая 2020

Я хотел бы создать уникальные файлы, добавляя к их имени номер, если это необходимо (аналогично тому, как браузеры обычно называют загруженные файлы).

Вот что я пытаюсь сделать, но с каталогами (используя библиотека <filesystem>):

#include <filesystem>
namespace fs = std::filesystem;

template <typename... Args> std::string concatenate(Args&&... args) {
    std::ostringstream sstr;
    (sstr << ... << std::forward<Args>(args));
    return sstr.str();
}

fs::path unique_dir(const fs::path &base, unsigned max_tries) {
    if(fs::create_directories(base))
        return base;

    for(unsigned i = 1; i < max_tries; ++i) {
        fs::path p = base;
        p += concatenate('_', i);

        if(fs::create_directory(p))
            return p;
    }

    throw std::runtime_error("unique_dir: gave up");
}

int main() {
    unique_dir("my_dir", 3); // creates my_dir
    unique_dir("my_dir", 3); // creates my_dir_1
    unique_dir("my_dir", 3); // creates my_dir_2
    unique_dir("my_dir", 3); // throws
}

Как сделать то же самое с файлами?

Некоторые значения точности:

  • Это не должен быть высокопроизводительным (это для очень холодной части кода)
  • Не кроссплатформенность хороша, если есть вариант для Linux, Windows и Ма c
  • Я не хочу использовать функцию типа mkstemp, которая потребует установки неидеального идентификатора в имени файла

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 01 мая 2020

Вот решение для систем POSIX, использующее open():

#include <fcntl.h> // open()
#include <unistd.h> // close()

fs::path unique_file(const fs::path &base, unsigned max_tries) {
    fs::path p(base);

    for(unsigned i = 1; i <= max_tries; ++i) {
        // O_CREAT | O_EXCL will create the file if it does not exist, and fail otherwise
        // 0666 : Read + Modify permissions for everybody
        int fd = open(p.c_str(), O_CREAT | O_EXCL, 0666);
        if(fd != -1) {
            if(close(fd) == -1) // We immediately close the file, which might be a waste
                throw fs::filesystem_error("unique_file: POSIX close() error", p, std::error_code(errno, std::generic_category()));

            return p;
        }

        // EEXIST is set if open() failed because the file already existed
        if(errno != EEXIST)
            throw fs::filesystem_error("unique_file: POSIX open() error", p, std::error_code(errno, std::generic_category()));

        errno = 0;
        p = base.parent_path() / base.stem();
        p += concatenate('_', i);
        p += base.extension();
    }

    throw std::runtime_error("unique_file: gave up");
}

Windows, кажется, предлагает функцию _sopen_s, которая предлагает та же комбинация _O_CREAT | _O_EXCL флагов.

0 голосов
/ 01 мая 2020

С каталогами вы проверяете, работало ли создание каталога. С помощью файлов вы можете добиться этого эффекта, проверив, указывает ли конкретный путь на существующий файл, а если нет, то создав его:

fs::path unique_file(const fs::path &base, unsigned max_tries) {
    if(!fs::exists(base)) {
       std::ofstream ofs(base);
       return base;
    }

    for(unsigned i = 1; i < max_tries; ++i) {
        fs::path p = base;
        p += concat() << '_' << i;

        if(!fs::exists(p)) {
            std::ofstream ofs(p);
            return p;
        }
    }

    throw std::runtime_error("unique_file: gave up");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...