回到首页 / 上级目录

文件处理

文件是存在磁盘中的数据。

系统会将文件中的数据加载到内存中,然后程序再开始处理数据。

文件类型

文件通过 FILE 结构体标识,FILE 的部分信息如下:

typedef struct {
    unsigned flags;        // 文件状态标志
    char fd;               // 文件描述符
    short bsize;           // 数据缓冲区的大小
    unsigned char *buffer; // 数据缓冲区的位置
} FILE;                    // FILE 定义在 stdio.h 头文件中

程序使用文件前需要打开文件,实质上是为文件创建相关的 FILE 记录。

程序使用文件后需要关闭文件,实质上是销毁 FILE 相关信息,并将内存中的新数据写回磁盘。

FILE *fp = fopen("file", "r");  // 以读的方式打开 file
if (fp == NULL) {
    printf("open failed\n");    // 若打开失败,则提示
} else {
    fclose(fp);                 // 否则关闭文件,关闭成功返回 0
}

打开方式

文件的打开方式包括读(r)、写(w)和追加(a)。

文件打开后,程序就可以进行相关的文件操作了。

FILE *in = fopen("file1", "r");  // 读的方式打开 file1
FILE *out = fopen("file2", "w"); // 写的方式打开 file2
char ch = fgetc(in);             // 从 file1 中读一个字符到 ch 中
while(ch != EOF) {               // 如果读取成功
    fputc(ch, out);              // 将 ch 写到 file2 中
    putchar(ch);                 // 打印 ch 到屏幕上
    ch = fgetc(in);              // 从 file1 中读下一个字符到 ch 中
}

其中 EOF 是定义为 -1 的全局变量。

字符串读写

C 语言提供了向文件读写一个字符串的函数:

char *fgets(char *s, int n, FILE *fp);
int fputs(char *s, FILE *fp);

格式化读写

C 语言提供格式化的方式读写文本文件:

int i = 3;
float f = 4.5;
fprintf(fp, "%d, %f", i, f);  // 以 3, 4.5 的格式将数输入文件中 
fcanf(fp, "%d, %f", &i, &f);  // 从文件中将 3 和 4.5 读出来

其中 fprintffscanf 存在 ASCII 码和二进制码之间的切换,如果内存和磁盘数据交换频繁,尽量不用。

二进制读写

可以采用 freadfwrite 进行二进制的读写。调用形式如下

fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp); 

例如读写数组、结构体:

typedef struct Person Person;
float f[10];
fread(f, 4, 10, fp); // 从文件 fp 中读 10 个浮点数

Person feng = {"feng", 20};
fwrite(&feng, sizeof(Person), 1, fp);  // 向文件 fp 中写入 feng 变量
fread(&feng, sizeof(Person), 1, fp);   // 从文件 fp 中读数据到 feng 变量

随记读写

C 语言支持从任意一个地方读取文件。

// 使文件位置标记返回文件开头
void rewind(FILE *stream); 
rewind(fp);

// 设置偏移位置,fromwhere 有三个值:0, 1, 2
int fseek(FILE *stream, long offset, int fromwhere);
fseek(fp, 100L, 0);  // 向前移到离文件开头 100 个字节处
fseek(fp, 50L, 1);   // 向前移到离当前位置 50 个字节处
fseek(fp, -10L, 2);  // 从文件末尾向后退 10 个字节

// 获取偏移位置,-1 表示函数时出错
long ftell(FILE *stream);

文件错误

C 提供一些函数用于检查输入输出函数调用时可能出现的错误:

// 执行 fopen 时 ferror 初值自动置 0
int ferror(FILE *stream);
if (ferror(fp)) printf("ferror\n");

// 将 ferror(fp) 置 0
void clearerr(FILE *stream);
clearerr(fp);