Files
cKit/Foundation/c_File.c
T
2026-08-29 01:50:50 +08:00

279 lines
7.5 KiB
C

#include <c_File.h>
#ifdef _WIN32
#include <windows.h>
#include <io.h> // 提供 _get_osfhandle
#include <direct.h>
#define C_ACCESS(path) _access(path, 0)
#define C_MAKE_DIR(path) _mkdir(path) // Windows 下创建目录
#else
#include <unistd.h> // 提供 fsync
#include <sys/stat.h>
#include <sys/types.h>
#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;
}