4.文件的顺序读取
4.1顺序读取函数
知识点:
输出:内存中输出到文件中/屏幕中(put,write、prinf)
fprintf从指定格式的数据内存得到数据输出到文件里、printf从指定格式的数据内存得到数据输出在屏幕上,
输入:从键盘/文件中读取到输入到内存中(read、get、scanf)
fscanf从文件得到输入到指定内存再进行printf,scanf从键盘上得到输入在指定内存
头文件都一样是#include<stdio.h>
功能 | 函数名 | 适用于 |
字符输入函数 | int fgetc ( FILE * stream ); | 所有输入流 |
字符输出函数 | int fputc(int character,FILE * stream) | 所有输出流 |
文本行的输入函数 | char * fgets ( char * str, int num, FILE * stream ); | 所以输入流 |
文本行的输出函数 | int fputs ( const char * str, FILE * stream ); | 所有输出流 |
格式化输入函数 | int fscanf ( FILE * stream, const char * format, ... ); | 所有输入流 |
格式化输出函数 | int fprintf ( FILE * stream, const char * format, ... ); | 所有输出流 |
二进制输入函数 | size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); | 文件 |
二进制输出函数 | size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ); | 文件 |
附:
流(stream):
当我们在写代码时,我们只需要把代码通过函数写好后在流中就可以实现我们需要的结果,这是因为C语言已经封装好的程序(流),一般来说当运行一个C语言程序时会自动打开
3个流(这也对应这scanf、printf、perror):
stdin - 标准输入(键盘) 类型 FILE *
stdout - 标准输出(屏幕) FLIE *
stderr - 标准错误(屏幕) FLIE *
所以我们在每次写程序控制文件的时候就必须先打开文件也就拥有指向这个文件的流
才能进行文件的输入(文件的打开方式"r",就对应像stdin的这样的输入流)、输出(打开文件的方式"w",对应输出流);所以上面的前6个里他们适合所有输入/输出流也就表明了,他们既可以对文件流中使用,也可以在标准流中使用
具体如下:
标准流时:
#include<stdio.h> int main() { int ch = fgetc(stdin); fputc(ch,stdout); return 0; }
细节:
文件流时:
4.1.1.fgetc、fputc
他们返回的都是整形也就是字符所对应的ASCII码值
int fgetc ( FILE * stream ) ; int fputc(int character,FILE * stream);
而他们的参数 FILE * stream 是指对应文件信息区的地址、int character 是所要写的字符的ASCII码值(直接传字符进去进去后都会转成ASCII码值)
练习:
将a~z的英文字母存放在文件中,并读取后打印
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { FILE* pf = fopen("test.txt", "w");//同过写的方式来打开文件,在每次写的时候都相当于重新创建一个文件,将原文件数据覆盖 if (pf == NULL)//防止开辟失败,访问NULL { perror("fopen"); return 1; } //使用文件 此处省略 for (int i = 'a'; i <= 'z';i++) { fputc(i, pf); } //关闭文件 fclose(pf); pf = NULL; FILE* pf1 = fopen("test.txt", "r");//通过读的方式来读取数据 if (pf1== NULL)//防止开辟失败,访问NULL { perror("fopen"); return 1; } //使用文件 此处省略 for (int i = 'a'; i <= 'z'; i++) { int ch = fgetc(pf1);//返回ASCII码值并打印 printf("%c ", ch); } //关闭文件 fclose(pf1); pf1 = NULL; return 0; }
对于fgetc来说他会按顺序一个个读取,先读最前面的读完后会自动指向下一个,当下一次再次读取时就会接着读下一个的了;对于fputc来说同样的他会一个个写,写完后自动往下指继续写,但假如重新打开文件(以写的方式)就会被覆盖。
如果读取失败/或写入失败的话将会返回EOF
4.1.2.fgets、fputs
char * fgets ( char * str, int num, FILE * stream );
他读取在stream中指定num个字符放在str中,若成功返回时返回:str、若失败了则返回EOF
参数 str 表示的是所要存/所要取字符串的位置的地址
参数 num 表示的是所要得到的字符的(num-1)个,为什么是num-1个是因为在读时字符串的最后要放上一个\0,所以会占掉一个位置;并且当遇到\n时会自动停止并且把\n换成\0,并且假如num的大小大于所要读大小会将多余的位置置为\0
参数 stream 表示的是文件信息区的地址
int fputs ( const char * str, FILE * stream );
他把str指向的字符串放到stream文件信息区上成功返回时返回:非负数;若失败了则返回EOF
这个就相对简单了就是将字符串的首地址传进去,以及文件信息区的位置
一行行的写,每一次写一行
int main() { FILE* pf = fopen("test.txt","w"); if (pf == NULL) { perror("fopen"); return 1; } fputs("hello world\n", pf);//文件输出函数 fputs("hello bit\n", pf); fclose(pf); pf = NULL; FILE* pf1 = fopen("test.txt", "r"); if (pf1 == NULL) { perror("fopen"); return 1; } char ch[20] = { 0 };// fgets(ch,12,pf1);//文件输入读取,并且此时字符串时12个字符那么就需要13个空间,因为fgets会在最后加上一个\0(但是此处只会那取 12 - 1 个字符 在最后12的位置要放上\0 ) printf("%s", ch);//hello world fgets(ch, 10, pf1); //此处因为一开始将前面的11(num - 1)个字符读去了在第一行还剩下一个\n所以就会把\n读取到,并且fgets遇到\n会自动停止并且将\n换成\0,并且因为只读一行所以会直接返回 printf("%s", ch);//打印\n fgets(ch, 11, pf1);//来到下一行,此时字符串有10个空间,第11的位置放\0,所要刚好 printf("%s", ch);//打印hello bit\n fclose(pf1); pf1 = NULL; return 0; }
4.1.3.fprintf、fscanf
int fprintf ( FILE * stream, const char * format, ... );
fprintf和printf用法几乎一样只需在最前面加上所要输出的文件信息区的位置即可
返回值:成功的话是返回总字符个数、失败则返回一个负数并且报错
int fscanf ( FILE * stream, const char * format, ... );
同样用法和scanf类似只是最前面加一个文件信息区的指针位置,
对于最后的.....是可变参数列表如:printf("%d %d %d",12,13,15); 此时参数可以同时多个输出
具体用法由下面代码展示:
struct S { int a; char arr[20]; float c; }; int main() { struct S s = { 100,"李四",120.00 }; FILE* pf = fopen("test.txt","w"); if (pf == NULL) { perror("fopen"); return 1; } fprintf(pf,"%d %s %f",s.a,s.arr,s.c);//先输出到pf文件上 fclose(pf); pf = NULL; FILE* pf1 = fopen("test.txt","r"); if (pf1 == NULL) { perror("fopen"); return 1; } fscanf(pf1,"%d %s %f",&(s.a),s.arr,&(s.c));//从文件中输入到内存中 printf("%d %s %f\n", s.a, s.arr, s.c);//从内存中打印 fclose(pf1); pf1 = NULL; return 0; }
4.1.4.fread、fwrite
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
此时fread、fwrite是二进制的输入输出函数,所以输入和输出都是以二进制形式的
同样ptr表示写入 / 读取 的位置,
size 表示的是该类型的大小,
count该类型的个数,
stream文件信息区的地址。
具体使用:
//学生信息管理系统铺垫 struct S { int a; char arr[20]; float c; }; int main() { struct S s = { 123,"李四",321 }; FILE* pf = fopen("test.txt", "wb"); if (pf == NULL) { perror("fopen"); return 1; } fwrite(&s, sizeof(struct S), 1, pf); fclose(pf); pf = NULL; struct S s1 = {0}; FILE* pf1 = fopen("test.txt", "rb"); if (pf1 == NULL) { perror("fopen"); return 1; } fread(&s1, sizeof(struct S), 1, pf1); printf("%d %s %f", s1.a, s1.arr, s1.c); fclose(pf1); pf1 = NULL; return 0; }
4.2sscanf、sprintf
知识点:
int sscanf ( const char * s, const char * format, ...);
sscanf从s处数据得到输入到指定内存,把字符串转化成一个格式化的数据
int sprintf ( char * str, const char * format, ... );
sprintf从指定内存中获得输出在str处,打一个格式化的数据转化成字符串
由此可以总结出:
scanf、printf,fscanf,fprintf,sscanf,sprintf
他们的不同类型的区别:
scanf类型:在最前面的参数是:所要获取数据的地方,如scanf的stdin(在此函数因为每次程序运行都会自动打开所以省略了),同样的fscanf的文件、stdin所有流、sscanf 的指定获得处 ;中间参数(const char * s)是指定的格式,最后是存放这些格式(...)的数据的地址,输入函数
printf类型:在最前面的参数(...)是:输出指定格式数据的位置,如prinf中的stdout;fprintf的所有流,sprintf的指定输出处; sprint中间参数(fchar * str)是指定的格式,最后的参数是从哪里获取的数据(...),输出函数
细节:
深入了解scanf、prinf后就能写出
int main() { struct S s = { 123,"李四",321.0 }; char tmp[100] = { 0 }; sprintf(tmp , "%d %s %f",s.a,s.arr,s.c);//将结构体s的数据输出tmp内 printf("%s\n", tmp); struct S s1 = { 0 }; sscanf(tmp, "%d %s %f", &(s1.a), s1.arr, &(s1.c));//将tmp得到的数据输入到结构体s1内 printf("%d %s %f", s1.a, s1.arr, s1.c); return 0; }