Ответ
Для атомарного создания файла только при его отсутствии (чтобы избежать состояния гонки между проверкой exists и созданием) необходимо использовать низкоуровневые системные вызовы или флаги файловой системы.
1. Использование системного вызова open() с флагами O_CREAT | O_EXCL (POSIX, наиболее надёжно):
Этот метод атомарен даже в многопоточной и многопроцессной среде.
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <iostream>
int fd = open("myfile.txt", O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd == -1) {
if (errno == EEXIST) {
std::cout << "Файл уже существует.n";
} else {
perror("open failed");
}
} else {
std::cout << "Файл успешно создан.n";
close(fd); // Не забываем закрыть дескриптор
}
2. Использование std::ofstream с флагами (C++17, но не полностью атомарно на всех файловых системах):
#include <fstream>
#include <iostream>
std::ofstream file("myfile.txt", std::ios::out | std::ios::app); // app не перезаписывает
// Но это не гарантирует, что файл не существовал до этого.
// Более точный, но всё же не атомарный способ (C++17):
#include <filesystem>
namespace fs = std::filesystem;
if (!fs::exists("myfile.txt")) {
std::ofstream new_file("myfile.txt");
}
// !ВНИМАНИЕ: Между проверкой exists и созданием файла другой процесс может успеть его создать.
Вывод: Для гарантированно атомарного создания в конкурентной среде следует использовать системный вызов open() с флагами O_CREAT | O_EXCL.
Ответ 18+ 🔞
А, слушай, тут такая тема интересная подъехала — как файл создать, чтобы не было этой ебалы с гонками, когда один процесс проверяет, существует ли файл, а второй уже успевает его создать в этот момент. Это же классика, ёпта! Представь: ты такой проверяешь if (!exists()), а в это время какой-то пидарас шерстяной уже успевает свой файл запилить, и твоя программа потом создаёт второй, перезаписывает или вообще пиздец какой-то. Удивление пиздец, когда потом разбираешься, откуда баги лезут.
Так вот, самый надёжный способ, чтобы атомарно создать файл только если его нет — это использовать низкоуровневый системный вызов open() с флагами O_CREAT | O_EXCL. Это, блядь, как волшебство: система сама проверяет существование и создаёт за одну операцию, без промежутков, где можно влезть. Даже если два процесса одновременно вызовут — один получит файл, а второй ошибку EEXIST. Красота, ядрёна вошь!
Вот смотри, как это выглядит в коде:
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <iostream>
int fd = open("myfile.txt", O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd == -1) {
if (errno == EEXIST) {
std::cout << "Файл уже существует.n";
} else {
perror("open failed");
}
} else {
std::cout << "Файл успешно создан.n";
close(fd); // Не забываем закрыть дескриптор
}
Видишь? Всё чётко. Создалось — ок. Не создалось, потому что уже есть — получишь ошибку и обработаешь. Никаких промежуточных состояний, где можно проёбаться.
А теперь, бля, смотри на то, что многие пытаются делать на C++ через std::ofstream или std::filesystem. Выглядит вроде прилично, но доверия ебать ноль в конкурентной среде!
#include <fstream>
#include <iostream>
std::ofstream file("myfile.txt", std::ios::out | std::ios::app); // app не перезаписывает
// Но это не гарантирует, что файл не существовал до этого.
// Более точный, но всё же не атомарный способ (C++17):
#include <filesystem>
namespace fs = std::filesystem;
if (!fs::exists("myfile.txt")) {
std::ofstream new_file("myfile.txt");
}
// !ВНИМАНИЕ: Между проверкой exists и созданием файла другой процесс может успеть его создать.
Вот видишь этот комментарий? Он не просто так стоит. Между fs::exists и созданием файла — целая вечность в масштабах процессора! Какой-нибудь другой поток или процесс может в этот момент спокойно файл создать, и ты потом получишь два файла или конфликт. Это как раз та самая хитрая жопа, которая потом заставляет ночами дебажить.
Так что вывод, чувак, простой: если тебе нужна гарантия в многопоточной или многопроцессной среде — используй open() с O_CREAT | O_EXCL. Это старый, добрый, проверенный способ, который работает на уровне ядра, а не в пользовательском пространстве с его костылями. Всё остальное — это игра в русскую рулетку, только вместо пули — баг на проде.