文件读写原理

一、文件读写核心原理

  1. 数据流模型

    • 文件被抽象为连续的字节流

    • 通过文件指针FILE*)管理当前位置

    • 每次读写自动移动指针位置

  2. 缓冲机制

    // 默认缓冲区大小通常为 4096 字节(与内存页对齐)
    #define BUFSIZ 4096 
    • 写操作先存入缓冲区,满时触发真实磁盘写入

    • 读操作预加载数据块到缓冲区

  3. 文本模式 vs 二进制模式

二、主要读写函数对比

1. 字符级读写

int fputc(int c, FILE *stream);  // 写单个字符
int fgetc(FILE *stream);         // 读单个字符
// 示例:逐字符复制文件
while ((ch = fgetc(src)) != EOF) {
    fputc(ch, dest);
}

2. 行级读写

char *fgets(char *s, int size, FILE *stream); // 读取一行(包含换行符)
int fputs(const char *s, FILE *stream);      // 写入字符串(不自动加换行符)
// 示例:读取配置文件
char line[256];
while(fgets(line, sizeof(line), fp)) {
    printf("Line: %s", line);
}

3. 格式化读写

int fprintf(FILE *stream, const char *format, ...); // 格式化写入
int fscanf(FILE *stream, const char *format, ...);  // 格式化读取
// 示例:写入结构体
struct Student s = {101, "Alice", 85.5};
fprintf(fp, "%d %s %.1f\n", s.id, s.name, s.score);

4. 块数据读写

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
// 示例:读写二进制数据
float data[100];
fwrite(data, sizeof(float), 100, fp);  // 写入100个float
fread(data, sizeof(float), 100, fp);   // 读取100个float

三、关键控制函数

1、文件定位

int fseek(FILE *stream, long offset, int whence);  // 移动指针
long ftell(FILE *stream);                           // 获取当前位置
void rewind(FILE *stream);                          // 重置到文件开头

whence 参数:

  • SEEK_SET:文件开头

  • SEEK_CUR:当前位置

  • SEEK_END:文件末尾

2、缓冲区控制

int fflush(FILE *stream);  // 强制刷新缓冲区
setvbuf(fp, NULL, _IONBF, 0); // 关闭缓冲(实时写入)

四、完整工作流程示例

1. 二进制文件读写(结构体存储)

#include <stdio.h>

typedef struct {
    int id;
    char name[20];
    double score;
} Student;

int main() {
    FILE *fp = fopen("students.dat", "wb+");
    if(!fp) { perror("Error"); return 1; }

    // 写入数据
    Student s1 = {1001, "Alice", 92.5};
    Student s2 = {1002, "Bob", 88.0};
    fwrite(&s1, sizeof(Student), 1, fp);
    fwrite(&s2, sizeof(Student), 1, fp);

    // 定位到开头并读取
    rewind(fp);
    Student temp;
    while(fread(&temp, sizeof(Student), 1, fp) == 1) {
        printf("ID: %d, Name: %s, Score: %.1f\n", 
               temp.id, temp.name, temp.score);
    }

    fclose(fp);
    return 0;
}

2. 文本文件处理(CSV格式)

void write_csv(const char* filename) {
    FILE *fp = fopen(filename, "w");
    if(!fp) return;

    fprintf(fp, "Name,Age,Salary\n");
    fprintf(fp, "Alice,28,8500.50\n");
    fprintf(fp, "Bob,35,12000.75\n");
    
    fclose(fp);
}

void read_csv(const char* filename) {
    FILE *fp = fopen(filename, "r");
    if(!fp) return;

    char header[100];
    fgets(header, sizeof(header), fp); // 跳过标题行

    char name[20];
    int age;
    float salary;
    while(fscanf(fp, "%[^,],%d,%f\n", name, &age, &salary) == 3) {
        printf("%s: %d years, $%.2f\n", name, age, salary);
    }

    fclose(fp);
}

五、关键错误处理

1、读写返回值校验

// fread/fwrite 返回成功读写的元素数量
size_t count = fwrite(buffer, sizeof(int), 100, fp);
if(count != 100) {
    // 处理写入不完整的情况
}

// 检查文件结束
if(feof(fp)) {
    printf("Reached end of file\n");
}

// 检查错误标志
if(ferror(fp)) {
    perror("File error");
    clearerr(fp); // 清除错误标志
}

2、二进制文件校验

// 写入文件头标识
const char MAGIC[4] = {'F', 'I', 'L', 'E'};
fwrite(MAGIC, sizeof(char), 4, fp);

// 读取时校验
char header[4];
if(fread(header, 1, 4, fp) != 4 || 
   memcmp(header, MAGIC, 4) != 0) {
    printf("Invalid file format\n");
}

六、性能优化技巧

1、批量读写

// 低效方式:逐个写入
for(int i=0; i<1000000; i++) {
    fwrite(&data[i], sizeof(int), 1, fp);
}

// 高效方式:批量写入
fwrite(data, sizeof(int), 1000000, fp);

2、内存映射文件(POSIX系统):

int fd = open("largefile.bin", O_RDWR);
void *map = mmap(NULL, file_size, PROT_READ|PROT_WRITE, 
                MAP_SHARED, fd, 0);
// 直接操作内存即可修改文件
munmap(map, file_size);
close(fd);

3、设置合适缓冲区

char buf[16384]; // 16KB 缓冲区
setvbuf(fp, buf, _IOFBF, sizeof(buf)); // 全缓冲模式

七、常见问题排查

1、文本文件数值错误

  • 现象:读取的浮点数精度异常

  • 原因:使用%f读取时未考虑本地化设置

  • 解决:

setlocale(LC_NUMERIC, "C"); // 强制使用点号小数点

2、结构体对齐问题

  • 现象:跨平台读取二进制结构体出错

  • 解决:

#pragma pack(push, 1) // 1 字节对齐
typedef struct { ... }
#pragma pack(pop)

3、文件截断问题

  • 现象:新写入内容比原内容短时残留旧数据

  • 解决:

freopen(NULL, "wb", fp); // 重新以写模式打开清空文件

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值