关于使用ftell()检查二进制文件是否为空的技术疑问
关于用
ftell()判断文件是否为空的疑问解答 咱们先把核心问题拆清楚:你混淆了**「文件无法打开/创建」和「文件存在但无内容」**这两个完全不同的场景,这也是你困惑的根源。
1. 先搞懂fp == NULL到底代表什么
fopen()返回NULL,只说明系统无法完成打开/创建文件的操作,比如:
- 用
r/r+b模式打开一个不存在的文件 - 文件存在但你没有读写权限
- 磁盘空间满了、路径不存在等系统级错误
但如果你用wb/w+b/a这类模式调用fopen():
- 不管文件原本是否存在,系统都会帮你创建一个新文件(如果原本有文件,直接截断为空)
- 这时候
fp几乎不会返回NULL(除非遇到上面说的系统级错误)
换句话说,你写的那段fp==NULL的代码,本质是在处理文件创建失败的异常,完全没法判断「文件原本有没有数据」——因为用wb模式的话,就算原本有数据也被你清空了,根本不符合你「保留原有数据并插入新结构体」的需求。
2. 为什么需要ftell()?
你的核心需求是:如果文件已有数据,就读取所有内容后插入新结构体;如果是空文件(包括刚创建的),直接写入。这时候你不能用wb模式,得用r+b(打开已有文件,支持读写)或者先判断文件存在性再选择模式。
当你成功用r+b打开文件后(fp非NULL),ftell()的作用就体现了:
- 调用
fseek(fp, 0, SEEK_END)把文件指针移到末尾,再用ftell()就能拿到当前文件的字节大小 - 如果返回值是0,说明文件是空的,直接写结构体就行
- 如果返回值大于0,说明文件里已有数据,这时候你就需要先读取所有结构体,找到正确插入位置后再写回文件
举个符合你需求的简化流程代码:
typedef struct { int id; char name[20]; } MyStruct; // 先尝试打开已有文件(读写模式,不会截断原有内容) FILE *fp = fopen("data.bin", "r+b"); if (fp == NULL) { // 文件不存在,创建新文件 fp = fopen("data.bin", "wb"); if (fp == NULL) { perror("创建文件失败"); return 1; } // 空文件直接写入 MyStruct new_struct = {1, "Alice"}; fwrite(&new_struct, sizeof(MyStruct), 1, fp); fclose(fp); return 0; } // 文件已存在,判断是否为空 fseek(fp, 0, SEEK_END); long file_size = ftell(fp); rewind(fp); if (file_size == 0) { // 空文件直接写入 MyStruct new_struct = {1, "Alice"}; fwrite(&new_struct, sizeof(MyStruct), 1, fp); } else { // 读取所有已有结构体 int struct_count = file_size / sizeof(MyStruct); MyStruct *all_structs = malloc(struct_count * sizeof(MyStruct)); if (all_structs == NULL) { perror("内存分配失败"); fclose(fp); return 1; } fread(all_structs, sizeof(MyStruct), struct_count, fp); // 假设按id排序找到插入位置(这里需要你自己实现逻辑) MyStruct new_struct = {2, "Bob"}; int insert_pos = 0; for (; insert_pos < struct_count; insert_pos++) { if (all_structs[insert_pos].id > new_struct.id) { break; } } // 写回文件:先写插入位置前的内容,再写新结构体,最后写剩余内容 fseek(fp, 0, SEEK_SET); fwrite(all_structs, sizeof(MyStruct), insert_pos, fp); fwrite(&new_struct, sizeof(MyStruct), 1, fp); fwrite(all_structs + insert_pos, sizeof(MyStruct), struct_count - insert_pos, fp); free(all_structs); } fclose(fp);
3. 总结一下
fp == NULL是用来处理文件打开/创建失败的异常情况,和文件是否有内容无关ftell()是在文件成功打开后,用来判断文件是否已有数据的关键依据,这才是你需求里需要的判断条件- 如果你误用
wb模式,会直接清空原有文件,完全违背了「保留数据并插入」的初衷
内容的提问来源于stack exchange,提问作者Ammar Brk
相关产品推荐
相关产品推荐

