#include #ifdef _WIN32 #include #include // 提供 _get_osfhandle #include #define C_ACCESS(path) _access(path, 0) #define C_MAKE_DIR(path) _mkdir(path) // Windows 下创建目录 #else #include // 提供 fsync #include #include #define C_ACCESS(path) access(path, F_OK) #define C_MAKE_DIR(path) mkdir(path, 0755) #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; }