目录
前言:
大家好呀!emmm,好多天都没有发博客了,实在是罪过,今天呢就把C语言中文件操作的相关知识来给大家做做总结,还请大佬帮忙斧正喔,十分感谢!
另外呢,自己也是达成了第一步的初步目标,来到了500粉,也进入了新晋作者榜,真的很感谢各位帅哥美女的支持,自己也会更加努力,向下一目标进发,冲冲冲!
一,为什么使用文件
一般我们写的程序的数据都是存储在内存上,而随着程序的运行结束,内存的空间也就会还给操作系统,记录的数据也就随之清空,所以我们为了达到数据的持久化,就需要将数据存储到文件里面,也就是保存到硬盘上。
二, 什么是文件
磁盘上的文件就是文件。
在程序设计中,我们一般谈的文件分为两种:程序文件与数据文件
2.1 程序文件
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
2.2 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
2.3 文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。文件名包含3部分:文件路径+文件名主干+文件后缀 例如: c:\code\test.txt 为了方便起见,文件标识常被称为文件名。
三,文件的打开和关闭
3.1 文件指针
定义:缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。(文件信息区的实质就是一个结构体变量,里面存放着有关文件的所有信息)
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
FILE* pf;//文件指针变量
我们通过文件指针就可以找到文件信息区,进而就可以通过文件信息区里面的信息找到相应的文件。
3.2 文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。 ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
例如:
#include<stdio.h>
int main() {
//打开文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
对于fopen而言,打开文件成功就会返回一个指向该文件的指针,打开失败就会返回一个空指针,所以在打开后也要进行判空的操作。对于fclose而言,他的作用其实就和free有点相似,将文件关闭,然后把这个文件指针置空。注意不要给fclose传空指针。
在打开文件的时候,有一个可能会犯的错误提醒一下,就是在不显示扩展名的情况下创建文件后,不小心输入了扩展名,然后程序打开文件说找不到文件,如下:
文件的打开方式如下:
四,文件的顺序读写
4.1,相关函数
4.2,字符输入输出函数
4.2.1,fputc
int fputc( int c, FILE *stream );
4.2.2,fgetc
int fgetc( FILE *stream );
//返回值是读取到的字符对应的ascii码值,当读取错误或者读取到文件末尾的时候会返回EOF。
4.2.3,使用
针对文件流:
针对标准输入输出流:
4.3,文本行输入输出函数
4.3.1,fputs
int fputs(const char* string,FILE* stream);
使用:
4.3.2,fgets
char *fgets( char *string, int n, FILE *stream );
//string是你将要存储读取字符的空间的地址,n是你读取的字符数目的最大值,stream是一个文件指针,说明你从哪去读数据。
使用
注意:最大字符个数里面是包含了\0的个数的,也就是说你实际能读取到的有效字符数是n-1。
4.4,格式化输入输出函数
对比认识fscanf,fprintf两个函数。
其实从定义上对比的话,两个函数其实相对于scanf与printf都只是多了个输入输出流,所以说,用法与scanf,printf基本是相同的。
4.4.1,fprintf
格式化输出函数,可以用于结构体等自定义类型数据写入文件。
4.4.2,fscanf
格式化输入函数,将文件内读取到的数据格式化输入到程序中进行赋值等一些操作。
4.4.3,对比几个函数
这里主要是介绍下最后两个函数sscanf与sprintf函数,它们的针对对象是字符串。
示例:
4.5,二进制输入输出函数
4.5.1,fread
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
//size指的是读取的每一个数据的大小,count指的是最多读取的数据的个数
以二进制的方式读文件,将文件里面的内容读取放到buffer指向的空间里面去。返回值是你实际读取到的数据的个数
4.5.2,fwrite
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
以二进制的方式写文件,将buffer里面的内容写到文件中去。返回值是你实际写入的数据的个数
示例:
五,文件随机读写
5.1 fseek
int fseek ( FILE * stream, long int offset, int origin );
//根据文件指针的位置和偏移量来定位文件指针。
示例:
//随机读
int main() {
//打开文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen");
return 1;
}
//读文件 文件里面放的是 abcdef
int ch = fgetc(pf);//字符读取函数,每读一次,文件指针会自动往后偏移,最开始是指向a
printf("%c\n", ch);
//利用fseek来重新定位文件指针
fseek(pf, 2, SEEK_SET);//此时文件指针会指向c
int ch1 = fgetc(pf);
printf("%c\n", ch1);
fseek(pf, 1, SEEK_CUR);//此时文件指针指向e
int ch2 = fgetc(pf);
printf("%c\n", ch2);
fseek(pf, -1, SEEK_END);//此时文件指针指向f
int ch3 = fgetc(pf);
printf("%c\n", ch3);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
//随机写
int main() {
//打开文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL) {
perror("fopen");
return 1;
}
//写文件 写入abcd
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
fseek(pf, 2, SEEK_SET);//此时文件指针指向c
fputc('x', pf);//此时文件内容被改成了abxd
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
解析:
5.2 ftell
long int ftell ( FILE * stream );//返回文件指针相对于起始位置的偏移量
接上面的随机写的代码,我们检测一下ftell的功能。
long pos = ftell(pf);
printf("%ld\n", pos);//输出3
5.3 rewind
void rewind ( FILE * stream );//让文件指针的位置回到文件的起始位置
六,文本文件与二进制文件
示例:如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而 二进制形式输出,则在磁盘上只占4个字节。如下代码测试:
7. 文件读取结束的判定
7.1 被错误使用的feof
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
判断文件是否结束的方法:
1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如: fgetc 判断是否为 EOF .
fgets 判断返回值是否为 NULL .
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:fread判断返回值是否小于实际要读的个数。
使用实例:(文本文件为例)
#include <stdio.h>
#include <stdlib.h>
int main()
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
fp = NULL;
}
ferror:判断文件是否是因为错误而结束,如果不是因为错误,则会返回0,否则返回非0值。
feof: 判断文件是否是因为到文件末尾而结束,如果是,则返回非0值,否则返回0。
八,文件缓冲区
ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
图示:
代码验证:
#include<stdio.h>
#include<windows.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠20秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(20000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘),手动刷新,即使缓冲区未满,也会加载到文件中
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠20秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(20000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区,所以这里再次睡眠排除fclose的干扰
pf = NULL;
return 0;
}
总结:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。例如数据的丢失。
九,通讯录三版链接
因为直接将所有源码复制过来篇幅会很长很长,所以这里就把自己的gitee代码仓库链接放到这里,大家有兴趣可以点开看看,三版的源码都已经打包成文件夹了。
结语:
今天的分享呢就到这了,如果大家觉得还不错的话,还请帮忙点点赞喔,十分感谢!