如何判断fopen是创建新文件还是打开已有文件?
解决方案
你当前使用的"ab+"模式调用fopen_s时,成功返回0的场景包含文件已存在并打开和文件不存在被创建两种情况,无法直接通过返回值区分。要实现仅在文件新建时执行操作的需求,可以参考以下两种方案:
一、标准C兼容方案(跨平台)
先尝试以只读模式打开文件,通过错误码判断文件是否存在:
#include <stdio.h> #include <errno.h> int main() { FILE *file = NULL; errno_t err; // 第一步:尝试只读打开文件,判断是否存在 err = fopen_s(&file, "test.txt", "r"); if (err == 0) { // 文件已存在,关闭只读句柄,再以目标模式打开 fclose(file); err = fopen_s(&file, "test.txt", "ab+"); if (err == 0) { // 文件已存在,不执行初始化操作 // ... 后续业务逻辑 } } else if (err == ENOENT) { // 文件不存在,创建并打开,执行初始化操作 err = fopen_s(&file, "test.txt", "ab+"); if (err == 0) { // 执行仅在文件新建时的操作 // ... 初始化逻辑 } } else { // 处理其他错误(如权限不足等) } // 记得关闭文件 if (file != NULL) { fclose(file); } return 0; }
注意:该方案存在竞态条件——在你判断文件不存在到创建文件的间隙,其他进程/线程可能创建了该文件,若需要严格保证原子性,建议使用系统特定方案。
二、系统特定原子方案(无竞态)
POSIX系统(Linux/macOS等)
使用open函数结合O_CREAT | O_EXCL标志,该组合会原子性地创建文件:如果文件已存在则直接失败,不存在则创建并打开,避免竞态:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main() { FILE *file = NULL; int fd = open("test.txt", O_CREAT | O_EXCL | O_APPEND | O_RDWR, 0644); if (fd == -1) { if (errno == EEXIST) { // 文件已存在,以目标模式打开 errno_t err = fopen_s(&file, "test.txt", "ab+"); if (err == 0) { // 不执行初始化操作 // ... 后续逻辑 } } else { // 处理其他错误 } } else { // 文件新建,将文件描述符转为FILE* file = fdopen(fd, "ab+"); if (file != NULL) { // 执行仅在文件新建时的操作 // ... 初始化逻辑 } else { close(fd); } } if (file != NULL) { fclose(file); } return 0; }
Windows系统
使用CreateFile函数结合CREATE_NEW标志,同样实现原子性判断与创建:
#include <stdio.h> #include <windows.h> #include <fcntl.h> #include <io.h> int main() { FILE *file = NULL; HANDLE hFile = CreateFileA("test.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_EXISTS) { // 文件已存在,以目标模式打开 errno_t err = fopen_s(&file, "test.txt", "ab+"); if (err == 0) { // 不执行初始化操作 // ... 后续逻辑 } } else { // 处理其他错误 } } else { // 文件新建,将句柄转为FILE* int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND | _O_RDWR); if (fd != -1) { file = _fdopen(fd, "ab+"); if (file != NULL) { // 执行仅在文件新建时的操作 // ... 初始化逻辑 } else { _close(fd); } } else { CloseHandle(hFile); } } if (file != NULL) { fclose(file); } return 0; }
内容的提问来源于stack exchange,提问作者HaeRim Lee
相关产品推荐
相关产品推荐

