目录
1.文件的基本概念:
2.文件的打开和关闭
3.文件的顺序读写
3.1:fputc/fgetc
3.2:fputs/fgets
3.3:fprintf/fscanf
3.4:fwrite/fread
3.5几组函数的对比
4.文件的随机读写
4.1fseek
4.2ftell
4.3rewind
5.文本文件和二进制文件
6.feof的正确使用
7.文件缓冲区
1.文件的基本概念:
一个文件名包含3部分:文件路径+文件名主干+文件后缀
例:C:\code\test.txt
2.文件的打开和关闭
每一个被打开的文件,在内存中都对应一个文件信息区,该文件信息区存储着该文件的基本属性和内容,这些信息保存在一个名为FILE的结构体变量中的。每次打开一个文件,都会返回该文件在内存中对应的文件指针,通过该指针我们可以修改文件内容。下面介绍文件打开和关闭的函数。
FILE * fopen ( const char * filename, const char * mode );
讯享网 讯享网int fclose ( FILE * stream );
打开文件用fopen,关闭文件用fclose。
打开文件的常用模式:
讯享网
最基本的代码:
int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 //... //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }
3.文件的顺序读写
关于文件顺序读写的函数:
在讲这些函数之前,我们先说一下”流“的概念。
在编程当中,”流“是一个抽象出来的东西,为什么会存在”流“呢?
这是为了减轻我们程序员的负担。最开始,当我们想要将内存中的内容输出到外部设备(屏幕,硬盘等)时,由于不同的外部设备有着不同的读写方式,导致输出内容时还得知道对应设备的读写方式,这就非常痛苦了。于是,在内存和外部设备之间加入”流“,我们程序员只需要将内容输出到”流“中,至于”流“中的数据怎么到外部设备,由我们的编译器帮我们实现,不用我们去操心了。
那么为什么打开文件时需要我们输入代码,打开什么什么文件,而输出到屏幕上,就不需要了呢?那是因为我们程序运行时,默认启动三个流,分别是:
- stdin:标准输入流(键盘)
- stdout:标准输出流(屏幕)
- stderror:标准错误流
3.1:fputc/fgetc
讯享网int fputc ( int character, FILE * stream ); int fgetc ( FILE * stream );
讯享网//fputc的用法: //写成功返回字符的ASCII //写失败返回EOF int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*int ch = 'a'; fputc(ch, pf); ch = 'b'; fputc(ch, pf);*/ //进阶的用法 int ch = 0; for (ch = 'a'; ch < 'z'; ch++) fputc(ch, pf); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

//fgetc的用法: //读取成功返回字符ASCII //读取到结尾返回EOF //读取失败返回EOF,并设置错误信息 int main() { //打开文件 FILE* pf = fopen("data.txt", "r");//这次是以读的方式了 if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*int ch = fgetc(pf); printf("%c ", ch); ch = fgetc(pf); printf("%c ", ch);*/ //进阶的用法 int ch = 0; while ((ch = fgetc(pf)) != EOF) printf("%c ", ch); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }


注意:在读取一个字符后,文件状态指针(可以理解为光标)就自动指向下一个位置
3.2:fputs/fgets
讯享网int fputs ( const char * str, FILE * stream ); char * fgets ( char * str, int num, FILE * stream );
如果说fputc/fgetc是输出/输入一个个字符,那么fputs/fgets就是输出/输入字符串了
讯享网int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 char arr[10] = "abcdefg"; fputs(arr, pf); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 char arr[10] = { 0 }; fgets(arr,10 ,pf);//输出abcdefg printf("%s", arr); fgets(arr, 7, pf); printf("%s", arr);//输出abcdef //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }
为什么num为7时只输出了abcdef?因为fgets只输入0~num-1个字符。
3.3:fprintf/fscanf
讯享网int fprintf ( FILE * stream, const char * format, ... ); int fscanf ( FILE * stream, const char * format, ... );
fprintf/fscanf:输出/输出格式化的数据
讯享网struct S { float f; char c; int n; }; int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 struct S s = { 3.14f,'a',19 }; fprintf(pf, "%f-%c-%d", s.f, s.c, s.n); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

struct S { float f; char c; int n; }; int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*struct S s = { 3.14f,'a',19 }; fprintf(pf, "%f-%c-%d", s.f, s.c, s.n);*/ struct S s = { 0 }; fscanf(pf, "%f-%c-%d", &(s.f), &(s.c), &(s.n));//要与写入文件时数据的格式一致 printf("%f-%c-%d\n", s.f, s.c, s.n); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

3.4:fwrite/fread
前面的几组文件操作函数都是输入输出文本文件的,而fwrite/fread则是输出/输入二进制文件。
讯享网size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ); size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
ptr指向一个数组,size指数组中每个元素的大小,count指要写/读多少个元素。
fwrite返回成功写入的个数;fread返回成功读取到的个数
讯享网int main() { //打开文件 FILE* pf = fopen("data.txt", "rb"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);*/ //由于写进去的是二进制,我们看不懂,但可以通过二进制读出来进行验证 int arr[10] = { 0 }; fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf); for (int i = 0; i < 10; i++) printf("%d ", arr[i]); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

3.5几组函数的对比
- printf/fprintf/sprintf
- scanf/fscanf/sscanf
用一张图来完整表示它们之间的区别
struct S { float f; char c; int n; }; int main() { struct S s1 = { 3.14f,'c',19 }; char arr1[20] = { 0 }; sprintf(arr1, "%f-%c-%d", s1.f, s1.c, s1.n); printf("%s\n", arr1); struct S s2 = { 0 }; char arr2[20] = "3.14-c-19"; sscanf(arr2, "%f-%c-%d", &(s2.f), &(s2.c), &(s2.n));//注意格式要一致 printf("%f-%c-%d", s2.f, s2.c, s2.n); return 0; }

4.文件的随机读写
上面的函数都是在文件中按照顺序进行读写,当我们想读写我们指定的位置,就需要用到随机读写的函数。下面介绍3种。
我们假设文件中的内容为abcdefg
4.1fseek
讯享网int fseek ( FILE * stream, long int offset, int origin );
功能:根据光标的位置和偏移量来定位文件指针
origin有三种模式:
- SEEK_SET:文件的最开始
- SEEK_CUR:当前光标的位置
- SEEK_END:文件的末尾
offset是偏移量
基本用法:
int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 int ch = 0; ch = fgetc(pf); printf("%c ", ch);//a ch = fgetc(pf); printf("%c ", ch);//b ch = fgetc(pf); printf("%c ", ch);//c fseek(pf, 0, SEEK_SET); ch = fgetc(pf); printf("%c ", ch);//a fseek(pf, -1, SEEK_CUR); ch = fgetc(pf); printf("%c ", ch);//a fseek(pf, -7, SEEK_END); ch = fgetc(pf); printf("%c ", ch);//a //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }
4.2ftell
讯享网long int ftell ( FILE * stream );
功能:返回当前光标相对于起始位置的偏移量
int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 int ch = 0; ch = fgetc(pf); printf("%c ", ch);//a ch = fgetc(pf); printf("%c ", ch);//b ch = fgetc(pf); printf("%c ", ch);//c int pos = ftell(pf); printf("%d\n", pos);//3 //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }
4.3rewind
讯享网void rewind ( FILE * stream );
功能:将光标移动到起始位置
int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 int ch = 0; ch = fgetc(pf); printf("%c ", ch);//a ch = fgetc(pf); printf("%c ", ch);//b ch = fgetc(pf); printf("%c ", ch);//c rewind(pf); ch = fgetc(pf); printf("%c ", ch);//a //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }
5.文本文件和二进制文件
数据以ASCII的形式存储到文件中,这时的文件叫做文本文件;
数据以二进制编码的形式存储到文件中,这时的文件叫做二进制文件。
6.feof的正确使用
很多人认为feof是用来判断文件是否读取结束的,这个说法并不是很准确,更明确的讲应该是判断当文件读取结束时,是因为遇到文件末尾而结束,还是因为读取过程中读取失败结束。
文本文件判断读取是否结束:
- fgetc:返回值是否为EOF
- fgets:返回值是否为NULL
二进制文件判断读取是否结束:
- fread:返回值是否小于要求读取到的个数
讯享网int main() { FILE* pf = fopen("data.txt", "r"); if (!pf) { perror("fopen"); return; } int ch = 0; while ((ch = fgetc(pf)) != EOF) { printf("%c ", ch); } if (ferror(pf)) { printf("I/O erroe when reading\n"); } else if (feof(pf)) { printf("ending of file\n"); } fclose(pf); pf = NULL; return 0; }
7.文件缓冲区
我们在读取文件或写入数据到文件时,数据并不是直接到文件或内存中的。
实际上,在内存和文件之间有个文件缓冲区,函数执行完数据是放在文件缓冲区当中的,当文件缓冲区满了,文件缓冲区被刷新了或文件关闭了,数据才会加载到我们想要的地方。
可以通过下面的代码来验证:
int main() { FILE* pf = fopen("data.txt", "w"); if (!pf) { perror("fopen"); return; } fputs("Hello file", pf); printf("打开文件,会发现文件没有内容\n"); Sleep(10000); fflush(pf); printf("此时文件有了内容\n"); Sleep(10000); fclose(pf); pf = NULL; return 0; }
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/51042.html