文件
文件的详细介绍 引子
文件.在我们前面学习通讯录时,程序运行起来时,可以给通讯录增删查改,但此时数据是存放在内存中,当程序退出时,通讯录的数据就消失了。 就很难受,为了使得数据持久化,我们就应该把数据存放在磁盘文件,存放到数据库等方式,磁盘上关机了重启还会在,使数据持久化,因此就需要用到文件
目录
文件的详细介绍
什么是文件
文件名
文件指针
文件的使用
文件的打开与关闭
顺序读写(流的概念)
fputc
fgetc
fputs fgets
fprintf,fscanf,sscanf,sprintf
fwrite fread
文件的随机读取
fseek ftell rewind
文件读取结束的判定
feof ferror
文件缓冲区
1 什么是文件
在磁盘上所有的东西都是文件,c盘包括桌面的, 但一般在程序设计时,分两种:程序文件和数据文件(从文件的功能进行分类的) 程序文件有源程序文件.c,目标文件.obj,可执行文件.exe 数据文件,我们写的数据
2.文件名
文件名,一个文件有唯一的文件标识。便于用户使用和识别(绝对路径) 文件名包含3个部分:文件路径+文件名主干+文件名后缀 如c:\code\test.txt,其中c:\code\是路径名,test叫文件主干名,.txt叫文件的后缀2
3.文件指针
4.文件指针(文件的打开与关闭) 每个被使用的文件都开辟了一个相应的文件信息区,用于存放文件的相关信息, (如文件的名字,文件状态及文件当前的位置)这些信息都保持在一个结构体变量中,该结构体类型名叫FILE 一般用FILE型指针来维护FILE结构的变量,这样使用起来更加方便 FILE* pf,pf是指向FILE型的变量,可以使用pf来指向某个文件信息区,通过文件信息区中的信息就可以访问该文件,即可以通过文件指针变量找到与他相关的文件,
4.文件的使用
文件在读写之前首先要打开文件fopen,在文件使用完之后要关闭文件fclose,fopen返回失败会返回一个空指针,因此每次使用都要判断
其中fopen原型是FILE * _fopen_(const char * path, const char * mode);
const char * path是使用的路径,即要在哪个文件中使用
const char * mode是fopen的打开形式
常见的模式有”r“,(read)以只读方式打开文件,该文件必须存在。
”w“(write)打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。
wb是只写打开或新建一个二进制文件,只允许写数据。
例如
文件的打开与关闭 1.0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> #include <stdlib.h> int main () { FILE *pf=fopen ("tst.dat" , "w" );* if (pf == NULL ) { perror ("fopen" ); return 1 ; } fclose (pf); pf = NULL ; return 0 ; }
1.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main () { FILE *pf=fopen ("D:\\2021\\class\\test.7\\test.dat" , "w" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fclose (pf); pf = NULL ; return 0 ; }
1.3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main () { FILE *pf = fopen ("tst.dat" , "r" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fclose (pf); pf = NULL ; return 0 ; }
文件的顺序读写 流
:流,高度抽象的概念,,我们写的程序有时想放屏幕,硬盘,u盘,光盘,网络,程序要操作各种硬件,也就需要各种的读写方式,因此在程序与硬件中抽象出流这个层次 我们只有把数据放到流里面去,写文件可以理解为文件流, c语言程序运行起来,就默认打开了3个流 stdin-标准输入流–键盘, stdout标准输出流–屏幕, stderr–标准错误流–屏幕,类型都是FILE*的
使用哪个流就在哪个流输出输入
代码2.0
fputc
int fputc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main () { FILE *pf = fopen ("test.dat" , "w" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fputc ('b' , pf); fputc ('i' , pf); fputc ('t' , pf); fclose (pf); pf = NULL ; return 0 ; }
2.1
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { fputc ('c' , stdout); fputc ('b' , stdout); fputc ('a' , stdout); return 0 ; }
2.2.
fgetc
int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // 文本中输入abcdef int main() { FILE *pf = fopen("test.dat" , "r" );// 以w的形式打开,即使原来有内容也会清空掉, if (pf == NULL) { perror("fopen" ); return 1 ; } // 读文件,fgetc从文件首字符中一个一个读取 //i nt ret=fgetc(pf);// 从文件流里面读一个字符,可以从文件,也可以从标准输入流中,文件字符输入,如果读取正常,会返回此字符的ascll值,如果读取失败,会返回EOF(-1 ) // printf("%c" , ret); // ret = fgetc(pf); // printf("%c" , ret);// 读取的都是test.dat int ret = fgetc(stdin);// 从标准屏幕输入流里面读一个字符,可以从文件,也可以从标准输入流中,文件字符输入,如果读取正常,会返回此字符的ascll值,如果读取失败,会返回EOF(-1 ) printf("%c" , ret); ret = fgetc(stdin); printf("%c" , ret);// 读取的都是test.dat,文件结束就会输出EOF为-1 fclose(pf); pf = NULL; return 0 ; }
fgets fputs
char *fgets(char *str, int n, FILE *stream)从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符
我们不仅可以一个字符一个字符的输入与输出,
我们也可以一行一行的输入与输出
可以用到fgets,fputs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main () { FILE*pf = fopen ("test.dat" , "w" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fputs ("abcdef\n" ,pf); fputs ("ghijk\n" , pf); fclose (pf); pf = NULL ; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <stdio.h> int main () { char arr[10 ] = { 0 }; FILE*pf = fopen ("test.dat" , "r" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fgets (arr, 4 , pf); printf ("%s\n" , arr); fgets (arr, 4 , pf); printf ("%s\n" , arr); fclose (pf); pf = NULL ; return 0 ; }
fprintf,fscanf,sscanf,sprintf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h struct s { char age[10 ]; int a; float f; };int main () { struct s a = { "hello" ,20 ,5.5f }; char buf[100 ] = { 0 }; struct s tmp = { 0 }; sprintf (buf, "%s %d %f" , a.age, a.a, a.f); printf ("%s\n" , buf); sscanf (buf, "%s %d %f" , tmp.age, &tmp.a, &tmp.f); printf ("%s %d %f" , tmp.age, tmp.a, tmp.f); return 0 ; }
fprintf(FILE *stream, const char *format,……)除去前面的流,后都和printf一样,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct a { char arr[10 ]; int num; float sc; };int main () { struct a s = { "abcd" ,10 ,5.5f }; FILE* pf = fopen ("test.dat" , "w" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fprintf (pf, "%s %d %f" , s.arr, s.num, s.sc); return 0 ; }
fscanf同scanf多了前面的流
1 2 3 4 5 6 7 8 9 10 11 12 13 int main() { struct a s = { "abcd" ,10 ,5.5 }; FILE* pf = fopen("test.dat" , "r" ); if (pf == NULL) { perror("fopen" ); return 1 ; } fscanf(pf, "%s %d %f" , s.arr, &(s.num), &(s.sc));// 格式化输出,写入pf文件流 printf ("%s %d %f" , s.arr, s.num, s.sc); return 0 ; }
fwrite fread
fwrite二进制输出 (const void *ptr, size_t size, size_t nmemb, FILE *stream)
把 ptr 所指向的数组中的数据写入到给定流 stream 中
size这是要被写入的每个元素的大小,以字节为单位
nmemb -- 这是元素的个数,每个元素的大小为 size 字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int main () { struct a s = { "abcd" ,10 ,5.5 }; FILE* pf = fopen ("test.dat" , "w" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fwrite (&s, sizeof (struct a), 1 , pf); fclose (pf); pf = NULL ; return 0 ; }
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
与fwrite等同二进制输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int main () { struct a s = { "abcd" ,10 ,5.5 }; FILE* pf = fopen ("test.dat" , "r" ); if (pf == NULL ) { perror ("fopen" ); return 1 ; } fread (&s, sizeof (struct a), 1 , pf); printf ("%s %d %f" , s.arr, s.num, s.sc); fclose (pf); pf = NULL ; return 0 ; }
文件的随机读取 fseek ftell rewind
文件的随机读取文件的随机读写,根据文件指针的位置和偏移量来定位文件指针 fseek(file stream,long offset,int origin)//offset偏移量,oringin起始位置, origin有三种状态,SEEK_CUR(从当前位置开始偏移),
SEEK_END(从文件末尾开始偏移)只能偏移负数,
SEEK_SET(从文件开头开始偏移
文件随机读取的函数有rewind,让文件指针重新回到起始地址
ftell判断此时文件指针对于起始位置的偏移量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int main() { FILE *pf=fopen("test.dat" , "r" ); if (pf == NULL) { perror("fopen" ); } //顺序读写,从头开始 int ch = fgetc(pf); printf ("%c" , ch); fseek(pf, 2 , SEEK_CUR);// 偏移的单位是字节,从当前位置向后偏移两个字节 ch = fgetc(pf); printf ("%c" , ch); ch = fgetc(pf); printf ("%c" , ch); int ret = ftell(pf); printf ("%d" , ret); // 让文件指针回到起始位置用rewind() rewind(pf);// 回到起始位置 int d = ftell(pf); printf ("%d" , d); return 0 ; }
数据文件与二进制文件
数据文件被称为二进制文件或二进制文件, 1000可以把内存中的数据转化为ascll值存储,文本文件,也可以以二进制存储就叫二进制文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main () { int a = 10000 ; FILE* pf = fopen ("test.dat" , "wb" ); if (pf == NULL ) { perror ("fopen" ); } fwrite (&a, sizeof (int ), 1 , pf); fclose (pf); pf = NULL ; return 0 ; }
文件读取结束的判定
文件读取结束的判定 fgetc函数在读取结束的时候,会返回eof,正常读取的是很好,返回的是读取的字符的ascll值 fgets函数在读取结束是,会返回null,正常读取的时候 ,返回的是字符串; fread在读取结束时,返回的是实际读取到的完整元素的个数,如果发现读取到的完整元素的个数小于指定的元素的个数,这就是最后一次读取, 判断文件结束的原因的函数是feof
ferror判断文件结束的原因是出现错误
写一个代码把text.txt 文件拷贝一份,生成text2.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int main () { FILE *pfd = fopen ("text.txt" , "r" ); if (pfd == NULL ) { return 1 ; } FILE *pw = fopen ("text2.txt" , "w" ); if (pw == NULL ) { fclose (pfd); pfd = NULL ; return 1 ; } int ch = 0 ; while ((ch = fgetc (pfd)) != EOF) { fputc (ch, pw); pw = NULL ; } if (feof (pfd)) { printf ("遇到文件结束标志,文件正常结束" ); } else if (ferror (pfd)) { printf ("文件读取失败结束" ); } fclose (pfd); pfd=NULL ;fclose (pw) pw=NULL ; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 enum { size = 5 };int main () { double a[size] = { 1 ,2 ,3 ,4 ,5 }; FILE *pf = fopen ("test.dat" , "wb" ); fwrite (a, sizeof *a, size, pf); fclose (pf); double b[size]; pf = fopen ("test.dat" , "rb" ); size_t ret_code = fread (b, sizeof *b, size, pf); if (ret_code == size) { puts ("arry read succseefully,contents" ); for (int n = 0 ; n < size; n++) { printf ("%f" , b[n]); putchar ('\n' ); } } else { if (feof (pf)) { printf ("error reading test.bin:unexpected end of file" ); } else if (ferror (pf)) { perror ("error reading test.bin" ); } } return 0 ; }
文件缓冲区
文件缓冲区 从内存中向硬盘输出数据,会先送到内存中的缓冲区,装满之后才会送到硬盘上, 如果硬盘向计算机读取数据,则硬盘文件中读取数据集输入到内存缓冲区,充满后 再从缓冲区逐个将数据送到程序数据区,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <windows.h> int main () { FILE *pf = fopen ("test.dat" , "w" ); fputs ("abcde" , pf); printf ("睡眠10秒-已经写数据,打开test.txt文件,文件没有内容\n" ); sleep (10000 ); printf ("刷新缓冲区\n" ); fflush (pf); printf ("再睡眠10秒,打开test.txt文件,有内容了" ); sleep (10000 ); fclose (pf); pf = NULL ; return 0 ; }
再学习完文件后,我们就可以将通讯录进行优化