前言
- 文件是当今计算机系统不可或缺的部分。文件用于储存程序、文档、数据、书信、表格、图片、视频和许多其他种类的信息。作为程序员,必须会编写创建文件和从文件读写数据程序。
- 操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件。文件在进行读写操作之前要先打开,使用完毕要关闭。
- 在C语言中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。
fopen和fclose函数
-
fopen函数原型:
- 主要功能是打开文件。需要头文件:stdio.h
- 函数声明如下:
FILE *fopen(const char *filename, const char *mode); //使用给定的模式 mode 打开 filename 所指向的文件
- filename指要打开的文件名称(可包含相对路径和绝对路径),mode指文件的访问模式。模式如下表:
模式 描述 r 打开一个用于读取的文件。该文件必须存在 r+ 打开一个用于更新的文件,可读取也可写入。该文件必须存在 rb+ 读写打开一个二进制文件,允许读写数据。文件必须存在 rw+ 读写打开一个文本文件,允许读和写 w 创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件 w+ 创建一个用于读写的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件 a 追加到一个文件。如果文件存在,写操作向文件末尾追加数据。如果文件不存在,则创建文件 a+ 以附加方式打开可读写的文件。如果文件存在,写操作向文件末尾追加数据。如果文件不存在,则创建文件 - fopen打开文件成功,返回有效FILE的有效地址,失败返回NULL。
-
fclose函数:
- 文件一旦使用完毕,应该用 fclose() 函数把文件关闭,以释放相关资源,避免数据丢失。fclose() 的用法如下:
int fclose(FILE *stream); //关闭流 stream,刷新所有的缓冲区
- 如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。
- 例子:
#include <stdio.h> int main() { FILE *fp; fp = fopen("file.txt", "r"); fprintf("打开一个只读文件file.txt"); fclose(fp); return(0); }
文本模式和二进制模式
-
文本文件和二进制文件:
- 所有文件的内容都以二进制形式(0或1)储存的。
- 文本文件:如果文件最初使用二进制编码的字符(如ASCII或Unicode)表示文本(就像C字符串那样),该文件就是文本文件,其中包含文本内容。
- 二进制文件:如果文件中的二进制代表机器语言代码或数值数据(使用相同的内部表示,假设,用于long或double类型的值)或图片或音乐编码,该文件就是二进制文件,其中包含二进制内容。
-
文本模式和二进制模式:
- 在二进制模式中,程序可以访问文件的每个字节。而在文本模式中,程序所见的内容和文件的实际内容不同。
- 在windows系统中,文本模式下,文件以" \r\n "代表换行。若以文本模式打开文件,并用fput等函数写入换行符" \n "时,函数会自动在" \n "前面加上" \r "。在windows读写一个二进制文件时,读写模式中参数一定要加" b ",防止系统添加无谓的" \r ",但读写一个文本文件时,就不要加" b ",这样可以不用单独处理" \r "。
- 在类Unix/Linux系统中,文本模式下,文件以" \n "代表换行,所以linux系统中在文本模式和二进制模式下并无区别。linux下读写模式中参数" b "是无效的,可忽略。
getc和putc函数
-
getc函数原型:
stat函数
-
函数原型:
- 主要获得文件属性。需要头文件:sys/types.h, sys/stat.h, unistd.h。
- 函数声明如下:
int stat(const char *file_name, struct stat *buf ); // 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
- 执行成功则返回0,失败返回-1,错误代码存于errno,包括:
ENOENT 参数file_name指定的文件不存在 ENOTDIR 路径中的目录存在但却非真正的目 ELOOP 欲打开的文件有过多符号连接问题,上限为16符号连接 EFAULT 参数buf为无效指针,指向无法存在的内存空间 EACCESS 存取文件时被拒绝 ENOMEM 核心内存不足 ENAMETOOLONG 参数file_name的路径名称太长数值
- stat结构体里面成员信息:
struct stat { dev_t st_dev; //文件的设备编号 ino_t st_ino; //节点 mode_t st_mode; //文件的类型和存取的权限 nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1 uid_t st_uid; //用户ID gid_t st_gid; //组ID dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号 off_t st_size; //文件字节数(文件大小) unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小) unsigned long st_blocks; //块数 time_t st_atime; //最后一次访问时间 time_t st_mtime; //最后一次修改时间 time_t st_ctime; //最后一次改变时间(指属性)
- 例子:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main() { struct stat st = { 0 }; stat("a.txt", &st); int size = st.st_size; // 得到文件的大小 printf("%d\n", size); return 0; }
-
stat,fstat,lstat函数区别:
- fstat,lstat函数原型:
int fstat(int filedes, struct stat *buf); int lstat(const char *path, struct stat *buf);
- 这三个函数的功能是一致的,都用于获取文件相关信息,但应用于不同的文件对象。对于stat函数中给出pathname参数,stat函数返回与此命名文件有关的信息结构,fstat函数获取已在描述符fields上打开文件的有关信息,lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由该符号链接引用文件的信息。
fread和fwrite函数
-
fread函数:
- fgets() 有局限性,每次最多只能从文件中读取一行内容,因为 fgets() 遇到换行符就结束读取。如果希望读取多行内容,需要使用 fread() 函数。
- fread() 函数用来从指定文件中读取块数据。所谓块数据,也就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据。
- 函数声明:其中,ptr 是指向带有最小尺寸 size*nmemb 字节的内存块的指针,可以是数组、变量、结构体等;size 是要读取的每个元素的大小,以字节为单位;nmemb 是元素的个数,每个元素的大小为 size 字节。stream 是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
-
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) //从给定流 stream 读取数据到 ptr 所指向的数组中
- 返回值:返回成功读写的块数,也即 nmemb。如果返回值小于 nmemb,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
-
fwrite函数:
- 函数声明:其中,ptr 是指向要被写入的元素数组的指针;size 是要被写入的每个元素的大小,以字节为单位;nmemb 是元素的个数,每个元素的大小为 size 字节。stream 是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) //把 ptr 所指向的数组中的数据写入到给定流 stream 中
- 返回值:返回成功读写的块数,也即 nmemb。如果返回值小于 nmemb,肯定发生了写入错误,可以用 ferror() 函数检测。
-
feof函数:
- 函数声明:
int feof(FILE *stream) //测试给定流 stream 的文件结束标识符
- 返回值:当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
-
例子:文件的复制
- WIN下输入:a a.avi b.avi ,可将a.avi复制到b.avi。由于一次只读一个,导致读取的时间很长,需优化。
/**** a.c ****/ #include <stdio.h> int main(int argc, char **args) { if(argc < 3) return 0; FILE *p = fopen(args[1], "rb"); if(p == NULL) return 0; FILE *p1 = fopen(args[2], "wb"); if(p1 = NULL) return 0; while(1) { char a = 0; fread(&a, 1, 1, p); if(feof(p)) break; fwrite(&a, 1, 1, p1); } fclose(p); fclose(p1); return 0; }
- WIN下输入:a a.avi b.avi ,可将a.avi复制到b.avi。由于一次读1024个字节,虽优化了时间,但可能导致复制过来的文件不一致,因为不能保证所有文件都是1024的倍数。
/**** a.c ****/ #include <stdio.h> int main(int argc, char **args) { if(argc < 3) return 0; FILE *p = fopen(args[1], "rb"); if(p == NULL) return 0; FILE *p1 = fopen(args[2], "wb"); if(p1 = NULL) return 0; while(1) { char a[1024] = { 0 }; fread(a, 1, sizeof(a), p); if(feof(p)) break; fwrite(a, 1, sizeof(a), p1); } fclose(p); fclose(p1); return 0; }
- WIN下输入:a a.avi b.avi ,可将a.avi复制到b.avi。采用堆的方式动态分配内存,极大缩短时间,为了防止文件过大,添加个阈值:最大一次只能发64K字节。
/**** a.c ****/ #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #define NUM 1024*64 // 64K int main(int argc, char **args) { if(argc < 3) return 0; FILE *p = fopen(args[1], "rb"); if(p == NULL) return 0; FILE *p1 = fopen(args[2], "wb"); if(p1 = NULL) return 0; struct stat st = { 0 }; stat(args[1], &st); int size = st.st_size; // 得到文件大小 if(size >= NUM) size = NUM; char *buf = malloc(size); // 根据文件的小,动态分配内存 while(!feof(p)) { int res = fread(buf, 1, size, p); fwrite(buf, 1, res, p1); } free(buf); fclose(p); fclose(p1); return 0; }
-
fseek函数:
- 函数声明:
int fseek(FILE *stream, long int offset, int whence) //设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数
- 其中,stream 是指向 FILE 对象的指针。offset 是相对 whence 的偏移量,以字节为单位。whence 是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
常量 描述 SEEK_SET 文件的开头 SEEK_CUR 文件指针的当前位置 SEEK_END 文件的末尾 - 返回值:如果成功,则该函数返回零,否则返回非零值。
- 例子:
#include <stdio.h> int main() { FILE *p1 = fopen("a.dat", "w"); char b[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; fwrite(b, 1, sizeof(b), p1); fclose(p1); FILE *p = fopen("a.dat", "r"); fseek(p, 2, SEEK_SET); //将文件指针从开始位移2个字节 char a[2]; fread(a, 1, sizeof(a), p); printf("%d, %d\n", a[0],a[1]); // 输出结果:3, 4 fseek(p, -2, SEEK_END); //将文件指针从末尾向初始位置方向位移2个字节 fread(a, 1, sizeof(a), p); printf("%d, %d\n", a[0],a[1]); // 输出结果:9, 10 fclose(p); return 0; }