文件操作让数据持久化+通讯录的实例演示

本文详细介绍了C语言中的文件操作,包括为何需要文件操作、文件定义、打开和关闭、随机读写、文本与二进制文件的区别、文件结束判定以及文件缓冲区的作用。重点讲解了数据持久化、文件指针和缓冲区对提高程序效率的影响。
摘要由CSDN通过智能技术生成

目录

一.为什么要有文件操作       

二.什么是文件

三.文件的打开和关闭

四.通讯录演示实例

下面的是顺序表(数组)实现的通讯录,如果想看看链表实现的通讯录可以点击这里喔喔c语言通讯录,但是链表-CSDN博客

五.文件的随机读写

六.文本文件和二进制文件

七.文件读取结束的判定

八.文件缓冲区


一.为什么要有文件操作       

        我们都知道,任何程序在运行的时候都是在内存中开辟空间的,但是这也存在一个问题:当电脑掉电或者程序退出的时候,我们所有的数据都会被清空,等到下一次运行程序的时候,数据早已不复存在了,(就比如我们之前写过的通讯录程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。)

        我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。而这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。

这也就违背了我们高效记录等等的目的。由此,文件(也被叫做文件流)便应运而生了。

使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。

二.什么是文件

        我们说:磁盘上的文件是文件。
但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
2.1 程序文件
        包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
2.2 数据文件
        文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。


那我们在这里讨论的是数据文件。

        在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。(搞清楚哪个是输入哪个是输出,具体怎么理解,且听我娓娓道来)
        其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。

三.文件的打开和关闭

        缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
        每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.

        当我们打开一个文件的时候,都会在内存中的文件信息区创建一个文件类型的结构体,他不需要我们理解其中的内核,只需要知道我们的操作都是在操作这个文件信息区上的内容。而该文件信息区又会和磁盘上的文件有所关联,所以只需要我们通过该文件信息区就能够访问磁盘中的文件,从而达到了数据持久化的目的。

例如,VS2013编译环境提供的 stdio.h 头文件中有以下的文件类型声明:

struct _iobuf {
    char *_ptr;
    int  _cnt;
    char *_base;
    int  _flag;
    int  _file;
    int  _charbuf;
    int  _bufsiz;
    char *_tmpfname;
   };
typedef struct _iobuf FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

下面我们来演示创建一个文件指针的打开和关闭

        文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。
打开方式如下:
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );

FILE * pf=fopen("test.txt","w");

        在这里,我们列举了一些文件的打开方式,值得注意的是:如果我们是以r(只读)的形式打开一个文件,一旦这个文件不存在,那么程序就会报错,而用w(写)的形式打开一个文件,若这个文件不存在的话,会在该程序的目录下创建一个新的文件,而且w会清除掉原来的所有数据,从头开始写入!!!如果我们要在后面追加数据的话一定是用a打开文件而不是w或者r类型

在知道了文件如何打开和关闭后,我们再来看看文件的顺序读写

这里列举了一些文件输入输出函数,那还可以搭配对应代码来了解,会有更深刻的认识

 对比一组函数:
        scanf/fscanf/sscanf
        printf/fprintf/sprintf

对于scanf,是标准输入流,即把我们从键盘上获取的数据,输入到内存中,

对于 fscanf,是所有的输入流,与scanf唯一不同的就是,他需要在调用的时候加上输入的地点(比如标准输入流stdin,或者文件流pf)(这里的pf是一个文件类型的指针)。而他一旦用stdin就与普通的scanf别无二致

对于sscanf则是将字符串类型,转换成一个格式化类型,比如可以将字符串类型的数据转换成一个结构体类型的数据,再存储在文件中。

这两组函数可以说是大体相同的使用方法,所以第二组不在赘述。

        然而,我们还需要了解的一点就是,sscanf和sprintf有什么用处呢?

        我们说过,他们无非就是将字符串类型的数据转化成格式化类型的数据,或者是将格式化类型的数据转换成字符串类型的数据。其实在我们的app上广有应用,比如我们在输入验证码,qq账号等等之类的信息时,我们输入的可能就是一个结构体类型的数据,但是这有可能不方便程序的读写,所以我们可以用sprintf将其转换成一个字符串类型的数据再来使用,就会方便许多。

四.通讯录演示实例

下面的是顺序表(数组)实现的通讯录,如果想看看链表实现的通讯录可以点击这里喔喔c语言通讯录,但是链表-CSDN博客

五.文件的随机读写

        我们在上面讲解了文件的读和写的操作,但是有一点并没有提到,就是在读文件的时候,其实相当于有一个光标(类似于鼠标)的东西在随着文件的读写而运动,(在fopen的时候光标在起始位置)每一次的读写都会自动跳到下一个数据的其实地址

那我们怎么样让光标指哪打哪,为我所用呢?

int fseek ( FILE * stream, long int offset, int origin );

fseek这个函数就是用来移动光标位置的函数,其中origin是你光标的位置,他有三种位置:

1.SEEK_SET

2.SEEK_END

3.SEEK_CURRENT

分别对应着起始位置,末位置和当前位置,

而offset是相对于origin的偏移量,我们只需要在此基础上输入offset的值就能让光标移动到我们想要的位置上,从而方便了我们的读和写。

long int ftell ( FILE * stream );

这个函数可以返回当前光标所在的位置,让我们可以看见而不至于摸瞎

void rewind ( FILE * stream );

rewind这个函数是让文件光标回到起始位置的函数,十分的简单,就不过多讲解,下面可以看看例子:

六.文本文件和二进制文件

        根据数据存储的形式,数据文件被分为文本文件和二进制文件。

        我们在之前的《数据在内存中的存储》

数据在内存中的存储方式及占位符的理解_the sun34的博客-CSDN博客中了解到数据是以二进制的形式写在内存中的,这个时候,如果我们不加以转换的就输出到外存中,那么外存中保存的就是二进制文件。

        而当我们对数据加以一定的处理,比如要求数据以ASCII码的值存储于外存中,则需要再储存前进行转换,这个以ASCII码值存储的文件就是文本文件。

那一个数据究竟是怎样存储于内存中的呢?

字符一律以ASCII码值存放,而数值型数据可以以ASCII存放,也可以用二进制的形式存放

如果要存一个数据10000,那么以ASCII存放就是五个字节,因为有5个字符类型的数据,而如果以二进制的形式存放,则就是四个字节,(下述图片来自于网络)

在这里,我们可以清晰的看到,10000这个数字以不同的存储方式,在内存中产生的效果

这个就是我们实际存储到文件中的数据,(如果是以二进制的形式存储的话)

那有人就又会有疑惑了?我们上面的明明就是一堆010101010啊,为什么是10 27 00 00呢?

其实在《数据在内存中的存储》数据在内存中的存储方式及占位符的理解_the sun34的博客-CSDN博客我们有详细的讲解,因为二进制并不方便我们的观察,所以电脑会自动显示为十六进制,而10 放在前面是因为vs这个编译器是小端存储模式的缘故。

七.文件读取结束的判定

        有这么一个函数,他常常被人使用,却又老是被人们错误的理解,他就是feof函数对于feof,我们需要牢记:在文件的读取中,不能用feof的返回值来判断文件是否结束,而是应当在文件读取结束的时候,用feof和ferror来判断是读取遇到文件尾结束,还是读取失败结束。

1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
fgetc 判断是否为 EOF .
fgets 判断返回值是否为 NULL .
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread判断返回值是否小于实际要读的个数。

文本文件的例子:

二进制文件的例子:

八.文件缓冲区


        ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。

为什么会有缓冲区呢?

其实缓冲区相信大家都听说过他的另外一个名字:缓存

简而言之:计算机计算是在cpu中进行的,而直接与cpu联系的是内存,因为硬件原因,内存的数据传输速度较快,而内存的容量又较小,不能很好让cpu的算力完美的发挥出来,于是缓存出现了,缓存能将硬盘中的数据大量的缓存起来,用于提供给内存,当cpu算完一次后,自动从内存中读取数据,而内存又自己从缓存中拿去数据,这样一来,就能够保证计算机cpu的算力的充分使用,从而提高了计算机的运算速度。

这里可以得出一个结论:
        因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致数据丢失,进而引发读写文件的问题。

       
 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值