#include #ifdef _WIN32 #include #include // 提供 _get_osfhandle #include #include #define C_ACCESS(path) _access(path, 0) #define C_MAKE_DIR(path) _mkdir(path) // Windows 下创建目录 #define sys_rmdir(path) _rmdir(path) #define PATH_SEP '\\' #else #include // 提供 fsync #include #include #define C_ACCESS(path) access(path, F_OK) #define C_MAKE_DIR(path) mkdir(path, 0755) #define sys_rmdir(path) rmdir(path) #define PATH_SEP '/' #endif /* ------------------------------------------------------------------------------------------------------------------ */ /* */ #if defined(C_PLATFORM_IS_64BIT) // 跨平台 64 位 fseek 封装 C_STATIC_FORCE_INLINE int fn_fseek(FILE* fp, long long offset, int whence) { #ifdef _WIN32 return _fseeki64(fp, (__int64)offset, whence); #else // 在定义了 _FILE_OFFSET_BITS=64 的情况下,fseeko 接收 64 位的 off_t return fseeko(fp, (off_t)offset, whence); #endif } // 跨平台 64 位 ftell 封装 C_STATIC_FORCE_INLINE long long fn_tell(FILE* fp) { #ifdef _WIN32 return (long long)_ftelli64(fp); #else return (long long)ftello(fp); #endif } #elif defined(C_PLATFORM_IS_32BIT) C_STATIC_FORCE_INLINE int fn_fseek(FILE* fp, long long offset, int whence) { return fseek(fp, (long)offset, whence); } C_STATIC_FORCE_INLINE long long fn_tell(FILE* fp) { return (long long)ftell(fp); } #endif /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_err_t c_File_Open(c_File_t* file, const char* fileName, const char* mode) { if (!file || !fileName || !mode) { return C_ERR_FAIL; } file->fp = fopen(fileName, mode); if (!file->fp) { return C_ERR_FAIL; } return C_ERR_OK; } void c_File_Close(c_File_t* file) { if (file && file->fp) { fclose(file->fp); file->fp = NULL; } } c_err_t c_File_Read(c_File_t* file, void* buffer, c_size_t buffer_size, c_size_t *read_size) { if (!file || !file->fp || !buffer || buffer_size == 0) { if (read_size) *read_size = 0; return C_ERR_FAIL; } // 从文件流中读取 1 字节 * buffer_size size_t bytes_read = fread(buffer, 1, (size_t)buffer_size, file->fp); if (read_size) { *read_size = (c_size_t)bytes_read; } // 如果读取数量小于请求数量,需检查是出错还是到达文件末尾 if (bytes_read < (size_t)buffer_size) { if (ferror(file->fp)) { return C_ERR_FAIL; // 发生读取错误 } } return C_ERR_OK; } c_err_t c_File_Readline(c_File_t* file, void* buffer, c_size_t buffer_size, c_size_t *read_size) { if (!file || !file->fp || !buffer || buffer_size == 0) { if (read_size) *read_size = 0; return C_ERR_FAIL; } // fgets 会自动在末尾加上 '\0',并且最多读取 buffer_size - 1 个字符 char* result = fgets((char*)buffer, (int)buffer_size, file->fp); if (!result) { if (read_size) *read_size = 0; return C_ERR_FAIL; // 读取失败或到达文件末尾 } if (read_size) { *read_size = (c_size_t)strlen((char*)buffer); } return C_ERR_OK; } c_err_t c_File_Write(c_File_t* file, void* buffer, c_size_t buffer_size, c_size_t *write_size) { if (!file || !file->fp || !buffer || buffer_size == 0) { if (write_size) *write_size = 0; return C_ERR_FAIL; } size_t bytes_written = fwrite(buffer, 1, (size_t)buffer_size, file->fp); if (write_size) { *write_size = (c_size_t)bytes_written; } // 如果实际写入字节数不等于预期,说明写入失败(如磁盘满) if (bytes_written < (size_t)buffer_size) { return C_ERR_FAIL; } return C_ERR_OK; } c_err_t c_File_Seek(c_File_t* file, long long position) { if (!file || !file->fp) { return C_ERR_PARAM; } const int result = fseek(file->fp, (long)position, SEEK_SET); return (result == 0) ? C_ERR_OK : C_ERR_FAIL; } long long c_File_Size(c_File_t* file) { if (!file || !file->fp) { return 0; } // 1. 保存当前文件指针的位置 long long current_pos = fn_tell(file->fp); if (current_pos == -1LL) return 0; // 2. 将文件指针移到末尾 if (fn_fseek(file->fp, 0, SEEK_END) != C_ERR_OK) return 0; // 3. 获取末尾位置即为文件大小 long long size = fn_tell(file->fp); // 4. 恢复原先的文件指针位置 fn_fseek(file->fp, current_pos, SEEK_SET); return (long long)((size < 0) ? 0 : size); } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_err_t c_File_Flush(c_File_t* file) { if (!file || !file->fp) { return C_ERR_FAIL; } // Step 1: 先刷新 C 库自带的软件层缓冲区(将数据推入 OS 内核队列) if (fflush(file->fp) != 0) { return C_ERR_FAIL; } // Step 2: 获取底层文件描述符,强行驱动硬件刷盘 #ifdef _WIN32 // 从 C 语言的 FILE* 提取 Windows 系统的文件句柄 HANDLE int fd = _fileno(file->fp); if (fd < 0) return C_ERR_FAIL; HANDLE hFile = (HANDLE)_get_osfhandle(fd); if (hFile == INVALID_HANDLE_VALUE) return C_ERR_FAIL; // 强行驱动机械硬盘/SSD硬件将内核缓存写入物理介质 if (!FlushFileBuffers(hFile)) { return C_ERR_FAIL; } #else // Linux / macOS 平台直接提取文件描述符 int fd = fileno(file->fp); if (fd < 0) return C_ERR_FAIL; // 触发 POSIX 的 fsync 系统调用,阻塞直到硬件写入完毕 if (fsync(fd) != 0) { return C_ERR_FAIL; } #endif return C_ERR_OK; } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_bool_t c_File_IsExist(const char* fileName) { if (!fileName) { return C_FALSE; } // 参数 0 代表 F_OK,即只检查文件是否存在 // C_ACCESS 返回 0 表示文件存在且可访问,返回 -1 表示不存在或无权限 if (C_ACCESS(fileName) == F_OK) { return C_TRUE; } return C_FALSE; } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_err_t c_File_MkDirs(const char* path) { if (path == NULL || strlen(path) == 0) { return C_ERR_PARAM; } char temp_path[512]; snprintf(temp_path, sizeof(temp_path), "%s", path); size_t len = strlen(temp_path); // 统一将 Windows 的 '\\' 转换为 '/' 方便逐级切分 for (int i = 0; i < len; i++) { if (temp_path[i] == '\\') { temp_path[i] = '/'; } } // 确保路径以 '/' 结尾,方便循环逻辑处理 if (temp_path[len - 1] != '/') { if (len + 1 < sizeof(temp_path)) { temp_path[len] = '/'; temp_path[len + 1] = '\0'; len++; } else { return C_ERR_FAIL; // 路径超长 } } // 逐级探测并创建目录 for (int i = 0; i < len; i++) { // 每当遇到路径分隔符 '/' 时,尝试截断并创建当前层级目录 if (temp_path[i] == '/' && i > 0) { temp_path[i] = '\0'; // 临时截断字符串 // 检查当前级目录是否存在,不存在则创建 if (C_ACCESS(temp_path) != 0) { if (C_MAKE_DIR(temp_path) != 0) { return C_ERR_FAIL; // 创建失败 } } temp_path[i] = '/'; // 恢复分隔符 } } return C_SUCCESS; } c_err_t c_File_Rmdir(const char* path) { if (!path || path[0] == '\0') { return C_ERR_PARAM; } // 调用操作系统底层移除目录的 API int result = sys_rmdir(path); if (result == 0) { return C_ERR_OK; // 删除成功 } else { // 删除失败(可能是由于目录不存在、无权限、或者目录非空) return C_ERR_FAIL; } } c_err_t c_File_Rmdirs(const char* path) { if (!path || path[0] == '\0') { return C_ERR_PARAM; } c_err_t status = C_ERR_OK; #if defined(_WIN32) || defined(_WIN64) // ========================================== // Windows 平台的递归删除实现 // ========================================== char search_path[MAX_PATH]; // Windows 检索目录需要追加 "\\*" snprintf(search_path, sizeof(search_path), "%s\\*", path); WIN32_FIND_DATAA find_data; HANDLE h_find = FindFirstFileA(search_path, &find_data); if (h_find == INVALID_HANDLE_VALUE) { // 如果目录根本不存在,或者无法打开,尝试直接当做文件 remove 移除(处理符号链接等边界) return remove(path) == 0 ? C_ERR_OK : C_ERR_FAIL; } do { // 排除 Windows 的特殊目录 "." 和 ".." if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { continue; } // 拼接子项的完整路径 char sub_path[MAX_PATH]; snprintf(sub_path, sizeof(sub_path), "%s\\%s", path, find_data.cFileName); if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // 如果子项是目录,递归调用 status = c_File_Rmdirs(sub_path); } else { // 如果子项是普通文件,取消只读属性(防止因只读导致删除失败),然后将其删除 SetFileAttributesA(sub_path, FILE_ATTRIBUTE_NORMAL); status = (DeleteFileA(sub_path) != 0) ? C_ERR_OK : C_ERR_FAIL; } if (status != C_ERR_OK) { break; } } while (FindNextFileA(h_find, &find_data)); FindClose(h_find); #else // ========================================== // Linux / macOS (POSIX) 平台的递归删除实现 // ========================================== DIR* dir = opendir(path); if (!dir) { // 无法打开作为目录处理,尝试按普通文件物理删除 return remove(path) == 0 ? C_ERR_OK : C_ERR_FAIL; } struct dirent* entry; while ((entry = readdir(dir)) != NULL) { // 排除 Linux 的特殊目录 "." 和 ".." if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } // 拼接子项的完整路径 char sub_path[1024]; snprintf(sub_path, sizeof(sub_path), "%s/%s", path, entry->d_name); struct stat statbuf; if (stat(sub_path, &statbuf) == 0) { if (S_ISDIR(statbuf.st_mode)) { // 如果子项是目录,递归调用 status = c_File_Rmdirs(sub_path); } else { // 如果子项是文件,执行普通删除 status = (remove(sub_path) == 0) ? C_ERR_OK : C_ERR_FAIL; } } if (status != C_ERR_OK) { break; } } closedir(dir); #endif // ========================================== // 公共收尾:清空了内部所有子项后,最后删除外层空壳目录 // ========================================== if (status == C_ERR_OK) { if (sys_rmdir(path) != 0) { status = C_ERR_FAIL; } } return status; } c_err_t c_File_Delete(const char* fileName) { if (!fileName || fileName[0] == '\0') { return C_ERR_PARAM; } // 标准 C 库的 remove 能够直接删除文件(在某些平台上也能删除空目录) int result = remove(fileName); if (result == 0) { return C_ERR_OK; // 删除成功 } else { // 删除失败(文件不存在、或无权限、或文件正被某些操作系统强锁占用) return C_ERR_FAIL; } } #define COPY_BUFFER_SIZE 4096 c_err_t c_File_Copy(const char* srcPath, const char* destPath) { if (!srcPath || srcPath[0] == '\0' || !destPath || destPath[0] == '\0') { return C_ERR_PARAM; } FILE* src = fopen(srcPath, "rb"); if (!src) return C_ERR_NOTFOUND; FILE* dest = fopen(destPath, "wb"); if (!dest) { fclose(src); return C_ERR_FAIL; } char* buffer = (char*)malloc(COPY_BUFFER_SIZE); if (!buffer) { fclose(src); fclose(dest); return C_ERR_FAIL; } c_err_t status = C_ERR_OK; size_t bytes_read; // 循环读写块,避免一次性读入大文件撑爆堆内存 while ((bytes_read = fread(buffer, 1, COPY_BUFFER_SIZE, src)) > 0) { size_t bytes_written = fwrite(buffer, 1, bytes_read, dest); if (bytes_written < bytes_read) { status = C_ERR_FAIL; // 磁盘空间不足或写入错误 break; } } free(buffer); fclose(src); fclose(dest); // 如果中途失败,清理掉生成的不完整目标文件 if (status != C_ERR_OK) { remove(destPath); } return status; } c_err_t c_File_Move(const char* oldPath, const char* newPath) { if (!oldPath || oldPath[0] == '\0' || !newPath || newPath[0] == '\0') { return C_ERR_PARAM; } // 1. 尝试使用操作系统原生的轻量级重命名/移动 if (rename(oldPath, newPath) == 0) { return C_ERR_OK; } // 2. 跨分区保底策略:如果因为跨文件系统挂载点导致 rename 失败,则执行 复制 + 删除 c_err_t copy_err = c_File_Copy(oldPath, newPath); if (copy_err == C_ERR_OK) { if (remove(oldPath) == 0) { return C_ERR_OK; } else { // 如果删原文件失败,为了数据安全,把新拷过去的文件也撤销,避免数据状态不一致 remove(newPath); return C_ERR_FAIL; } } return C_ERR_FAIL; }