目录
1.为什么使用文件
我们知道在我们写一些具有记录性功能的一些程序时,我们添加或修改的成员只有在程序运行时才有,然而当我们退出程序时,这些内容就被内存回收了,因此为了使数据持久化,可以将数据放在磁盘文件或存放在数据库等方式。
2.文件是什么
磁盘上的文件是文件,但在程序设计中我们通常说的文件有两种:程序文件、数据文件(从文件功能角度分析)
(1)程序文件
包括源程序的文件(后缀为.c),目标文件(Windows环境后缀为.obj),可执行程序(Windows环境后缀为.exe)。
(2)数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件或者输出的文件内容
补充:
文件名包含三个部分:文件路径 + 文件名主干 + 文件后缀(后缀可以没有)
3.文件的打开和关闭
首先在了解文件打开前我们需要了解文件指针。当我们打开一个文件时,都会有一个文件信息区来存放相关的信息(文件的名字,文件的状态等)这些信息呢就是保存到我们的一个结构体变量中,该结构体是有系统声明的,取名为FILE。不同编译器的FILE类型包含可能不相同,但大都是一样的,每次使用一个文件,我们可以通过一个文件指针变量就能找到与它关联的文件。
1.打开文件
原型:FILE * fopen ( const char * filename, const char * mode )
打开文件,首先我们以什么方式打开,我们通常有以读的方式,以写的方式,以二进制方式读,以二进制方式写如以下表格中:
文件的使用方式 含义 如果指定文件不存在 “r” (只读) 为了输入数据,打开一个已经存在的文本文件 出错 “w” (只写) 为了输出数据,打开一个文本文件 建立一个新的文件 “a” (追击) 向文本文件尾部添加数据 建立一个新的文件 “rb” (只读) 为了输入数据,打开一个二进制文件 出错 “wb” (只写) 为了输出数据,打开一个二进制文件 建立一个新的文件 “ab” (追击) 向二进制文件尾部添加数据 出错 "r+" (读写) 为了读和写,打开一个文本文件 出错 "w+" (读写) 为了读和写,建立一个文本文件 建立一个新的文件 "a+" (读写) 打开一个文件,在文件尾部进行读写 建立一个新的文件 "rb+" (读写) 为了读和写,打开一个二进制文件 出错 "wb+" (读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件 "ab+" (读写) 打开一个二进制文件,在文件尾读和写 建立一个新的文件
2.关闭文件原型:int fclose ( FILE * stream )
//代码样例 int main() { FILE* pf = fopen("test.txt", "w"); if (!pf) { perror("fopen:"); return 0; } fclose(pf); pf = NULL; return 0; }
有了以上函数,就可以对文件的写和读
(1)fputc
原型:int fputc ( int character, FILE * stream )
fputc是一个字符一个字符的读取到文件中,读取失败返回EOF用例如下
//写文件字符 int main() { //struct S s = { 0 }; FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } char i = 0; for (i = 'a'; i <= 'z'; i++) { fputc(i, pf); } fclose(pf); pf = NULL; return 0; }
(2)fgetc
原型:int fgetc ( FILE * stream )
fgetc是文件一个一个字符的从文件中读取出来,读取失败返回EOF用例如下
//读文件字符 int main() { //struct S s = { 0 }; FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //char ch = fgetc(pf); //printf("%c", ch); char ch = 0; while ((ch = fgetc(pf)) != EOF) { printf("%c ", ch); } fclose(pf); pf = NULL; return 0; }
(3)fputs
原型:int fputs ( const char * str, FILE * stream )
fputs是用来一行一行的读取到文件中,读取失败返回EOF,用例如下
//写一行数据 int main() { //struct S s = { 0 }; FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } fputs("hello bit\n", pf); fclose(pf); pf = NULL; return 0; }
(4)fgets
原型:char * fgets ( char * str, int num, FILE * stream )
fgets是用来一行一行的读取文件,读取失败返回EOF,用例如下
//读一行数据 int main() { //struct S s = { 0 }; FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } char arr[20] = { 0 }; fgets(arr, 5, pf); //读的5个数据中,其中第五个是 '\0' printf("%s\n", arr);//所以相当于读了字符串中的 5-1 个字符 fclose(pf); pf = NULL; return 0; }
(5)fprintf
原型:int fprintf ( FILE * stream, const char * format, ... )
fprintf是用来格式化输出文件
//读一行数据 struct S { char name[20]; int age; float high; }; int main() { struct S s = { "zhangsan", 20, 1.87f}; FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } char arr[20] = { 0 }; fprintf(pf, "%s %d %f", s.name, s.age, s.high); fclose(pf); pf = NULL; return 0; }
(6)fscanf
原型:int fscanf ( FILE * stream, const char * format, ... )
是用来格式化输入文件
//读一行数据 struct S { char name[20]; int age; float high; }; int main() { struct S s = { 0 }; FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.high)); printf("%s %d %f\n", s.name, s.age, s.high); fclose(pf); pf = NULL; return 0; }
(7)fwrite
原型:size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream )
二进制形式输出
//二进制方式读一行数据 struct S { char name[20]; int age; float high; }; int main() { struct S s = { "zhangsan", 20, 1.87f }; FILE* pf = fopen("test.txt", "wb"); if (pf == NULL) { perror("fopen"); return 1; } fwrite(&s, sizeof(struct S), 1, pf); fclose(pf); pf = NULL; return 0; }
(8)fread
原型:size_t fread ( void * ptr, size_t size, size_t count, FILE * stream )
二进制形式输入
//二进制方式写一行数据 struct S { char name[20]; int age; float high; }; int main() { struct S s = { 0 }; FILE* pf = fopen("test.txt", "rb"); if (pf == NULL) { perror("fopen"); return 1; } fread(&s, sizeof(struct S), 1, pf); printf("%s %d %f\n", s.name, s.age, s.high); fclose(pf); pf = NULL; return 0; }
(9)sprintf
struct S { char name[20]; int age; float high; }; int main() { struct S s = { "zhangsan", 20, 1.87f }; char buf[100] = { 0 }; sprintf(buf, "%s %d %.2f\n", s.name, s.age, s.high); //sprintf函数的功能是将格式化的数据转化为字符串 printf("%s", buf); return 0; }
(10)sscanf
int main() { struct S s = { "zhangsan", 20, 1.87f }; struct S tmp = { 0 }; char buf[100] = { "zhangsan 20 1.87" }; //char buf[100] = { 0 }; //sprintf(buf, "%s %d %.2f\n", s.name, s.age, s.high); sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.high)); //将字符串转化为格式化的数据 printf("%s %d %.2f", tmp.name, tmp.age, tmp.high); return 0; }
总结:
scanf 是针对标准输入的格式化输入语句
printf 是针对标准输出的格式化输出语句
fscanf 是针对所有输入流的格式化输入语句
fprintf 是针对所有输出流的格式化输入语句
sscanf 是将字符串转化为格式化数据
sprintf 是将格式化数据转换为字符串
3.文件的随机读写
(1)fssek
原型:int fseek ( FILE * stream, long int offset, int origin )
fssek是根据文件指针的位置和偏移量来定位文件指针,它可以指定位置的读取文件,前提是要知道文件内容。(2)ftell
原型:long int ftell ( FILE * stream )
ftell是和上面的fssek函数内容相互照应,它能知道当前位置相对于起始位置的偏移量是多少。
(3)rewind
原型:void rewind ( FILE * stream )
rewind是用来回到起始位置,当我们已经不知道文件指针的位置时,就可以用这个函数来回到起始的位置。
4.文件读取结束的判定
文件结束有两个原因:1.就是文件读取到末尾。 2.就是文件读取错误,失败了从而结束。
feof函数会告诉我们文件读取结束的原因,而不能用feof函数的返回值直接用来判断文件的是否结束。
ferror函数会告诉我们文件读取失败时的原因。