【C语言】文件操作

一、啥是文件

我们在电脑中的数据都是存储在电脑的内存中的,但是当我们关机,程序退出等,那么此时内存就会回收,那么数据就没了,那么等我们再次开机,运行程序的时候,就没办法再找到这个程序的,那么我们需要将这个数据可以一直保存着,那么我们可以使用文件,将数据进行保存。

文件是存储在计算机上的信息集合,其可以分为很多种,比如:文本文档、图片、程序等

不过在程序中一般就两种文件:

1、程序文件

程序文件包括了:源文件程序(后缀为.c),目标文件(windows环境下后缀名为.odj),可执行文件(windows环境下后缀名为.exe)。

2、数据文件

文件的内容是程序运行中读写的数据。

二、数据文件

我们这次主要学习数据文件。

我们前面学习的知识上,所处理的数据的输入都是以键盘来输入的数据,然后使用显示器输出。

然后我们这个程序的结果输出到显示器后,我们结束这个程序,这个结果就没了,这是因为我们运行这个程序的时候,是将数据放在内存中的,那么程序运行结束后,那么这个内存就给释放了,然后就回收了。那么这个数据也就自然没了。

那么当我们需要将这个数据保存起来下次要使用的时候,那么此时我们就需要使用磁盘上的数据文件将其保存起来。当我们需要使用这个数据的时候,就从数据文件将其读取出到我们的内存中即可。

下面为文件的部分知识:

1、文件名

一个文件要有其唯一的标识,我们才能在需要其的时候可以快速,准确的拿到它。那么这个文件就需求其标识,这个标识就是我们常说的文件名,文件名字包含3个部分:文件路径+文件名主干+文件后缀

如:c:\code\test.txt

那么这个文件的路径就是:c:\code\

文件名主干:test

文件后缀:.txt

后缀.txt说明这个文件是一个文本文件,属于数据文件。

2、数据文件的分类

根据数据的组织形式,数据文件又被称为文本文件或二进制文件

2.1、文本文件

如果要求在内存上以ASCII的形式存储,那么则需要在存储之前进行转换,以ASCII的形式存储的文件就是文本文件。

也就是我们常见的文件,一打开我们就知道这个文件存储的内容是啥。

文本文件的常见后缀:.txt、.docx、.rtf

2.2、二进制文件

数据在内存中是以二进制的形式存储,如果不对其进行转换直接输出,那么就是二进制文件,那么里面就是01的组合,所以是看不出里面的内容的。

我们可以在vs中运行一下下面的代码。

下面我们运行这个代码:

我们可以看到我们这个程序的路劲下多了一个test的文件,那么我们上面的代码就是将a以二进制的形式写入到了这个文件中,那么我们这个路径下原来是没有这个文件的,那么我们这个程序就先创建了这个文件。那么我们打开这个文件看其内容。

可以看到上面并没有显示10000,这是因为我们写入是以为二进制的形式写入的。

三、文件的打开和关闭

1、流和标准流

流:

我们程序的数据需要输出到各种外部的设备,也需要从设备外部获取数据,不同的外部设备的输入输出的操作也是不同的,那么为了方便对各种设备进行操作,那么我们抽象出了流的概,我们可以把流 想象成流淌着字符的河。

C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。 ⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

标准流

我们上面提到了,如果需要输入或者读取信息,都需要打开流,然后进行操作,那么我们从键盘上输入信息,在屏幕上打印信息的时候为啥没有去进行打开流的操作呢?

那是因为C语言程序在启动的时候,默认打开3个标准流:

• stdin标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。

• stdout标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出 流中。

• stderr标准错误流,⼤多数环境中输出到显⽰器界⾯。

上面的三个是默认打开的标准流,我们使用scanf、printf等函数可以直接进行输入输出的操作。

stdin、stdout、stderr三个流的类型是:FILE*的指针,通常称为文件指针,对文件的操作就是使用文件指针进行操作。

2、文件指针

缓冲文件系统中,关键的概念就是"文件类型指针",简称文件指针,每个被使用的文件都会在内存中开辟一个相应的文件信息区,用来存放文件的相关信息。

然后这些文件是保存在一个结构体变量中的,这个结构体是由系统来声明的,这个结构体叫做:FILE。

例如我们在vs2013编译环境下提供的stdio.h头文件有下面的 文件类型:

不同的编译器的FILE类型包含的内容可能会有差异,不过都差不多。每当打开一个文件的时候,那么系统就会根据文件的情况自动创建一个FILE结构体的变量,然后填充其中的信息。

我们一般是通过FILE的指针来维护这个FILE的变量,这样使用起来方便。

下面我们可以创建一个文件指针变量:

FILE*pf;//文件指针变量

这里定义的pf是一个指向FILE的指针变量,可以让这个pf指针变量指向某个文件的文件信息区,然后通过这个指针我们可以访问这个文件。

也就是,我们可以通过这个文件指针变量,然后间接访问到对应的文件,如下:

3 、文件的打开和关闭

我们在对文件的读写都应该先打开文件,然后在使用结束的时候,应该将这个文件关闭,那么我们在程序中要如何进行此操作呢?

ANSI C规定了使用fopen函数来打开文件,然后使用fclos来关闭文件,在打开文件的同时,会返回一个FILE*的指针变量指向该文件,也就相当于建立了指针和文件的关系。

fopen函数的原型如下:

FILE*fopen(const char *filename,const char * mode);

如果文件打开成功,那么这个函数就会返回一个FILE*类型的指针,我们可以使用一个文件指针变量来接收,然后我们后面如果需要使用到这个文件,就可以通过这个指针来间接对这个文件进行操作。

如果文件打开失败,那么这个函数就会返回一个空指针NULL ,那么我们在使用这个函数打开一个文件的时候,最好是先判断其有没有打开成功。

其函数有两个参数,第一个参数是我们要打开的文件的名字,第二个参数就是我们打开文件的方式。

打开的方式有很多种,如下:

上面是文件的打开方式,现在我们对于文件的操作还没怎么涉及,现在只需了解一下即可。

下面我们通过这个函数以只读的方式来打开一个文件:

文件的关闭

文件的关闭我们使用fclose函数,其原型如下:

int fclose(FILE*stream);

可以看到其返回类型为一个整型类型,如果文件关闭成功,那么其就返回0,如果关闭失败,那么就返回EOF。

其参数就素我们要关闭的流,就是我们要关闭文件,那么就将这个文件的流,也就是对应的文件指针变量传过去。

不过要注意的是,我们将这个文件关闭后,原来指向这个文件的指针就会变成了野指针,所以我们在关闭这个文件后,最好再将这个指针置为空指针NULL。

如下:

如上就是文件的关闭操作了。

四、文件的顺序读写

文件的顺序读写是按照文件数据从头到尾进行读写的,读写操作也是由我们的函数来完成的。

如下:

下面我们就对这几个函数进行讲解:

1、fgetc函数

这个函数的功能是从流中获取一个字符,这里要注意的是,我们说的输入输出是对于内存来说的,我们从流中获取一个字符,对于流来说,即对于文件来说是输出,但是对于内存来说这个字符是从外面存储进了内存的,那么对于内存来说就是属于输入。

下面我们看看这个函数的原型:

int fgetc (FILE*stream);

这个函数的参数就是我们需要从那个流中或者字符,那么这里我们是需要对文件进行操作,那么这个参数就应该是一个文件指针变量。

然后我们看看其返回值,其返回的是一个整型,其意思就是,如果成功从文件中读取了一个字符,那么就返回这个字符的ascll码,如果读取失败,那么就返回EOF,那么下面我们使用其来看看:

我们首先要有下面的条件:

当前代码的路径下有要操作的文件,然后文件里面的内容是hello world!

然后我们现在只需要从这个文件中读数据,那么我们就以读的方式打开这个文件即可:

然后我们使用fgetc函数来实现函数的读取操作:

运行结果:

可以看到其读取了第一个字符。

那么如果我们需要将这个文件的所有内容都读取的话可以如下操作:

运行结果:

可以看到上面我们使用一个循环就将这个文件的中的所有字符都读取出来了。

2、fputc函数

这个函数和上面的fgetc函数很类似,其两个功能是相反的,fputc函数实现的功能是将字符写入文件中。

其原型如下:

int fputc(int character,FILE*stream);

它的第二个参数就是我们要输入的目标文件,然后第一个参数就是我们要输入的字符。

然后如果其写入成功,那么其返回值就是这个字符的ascll码,如果失败的话就返回EOF。不过这个函数的返回值没啥用。

下面我们使用这个函数,不过要注意的是,我们要是以写的方式打开文件,那么第一步就会清空这个文件的内容,然后再进行写的操作,那么如果我们不想文件的内容被清空,那么我们可以使用追加的方式打开。

下面我们先使用直接写的方式写入,即将上面的test.txt文件的内容先清空然后再进行写入:

我们运行后看看这个文件的内容有没有被改变:

可以看到这个文件的内容已经被我们先清空,后输入了。

3、fgets函数

这个函数是从这个文件中读出一行的信息。

原型:

chat *fgets(char * str,int num,FILE*stream)l;

它的第一个参数就是我们要读的一行的数据放入那个字符串,第二个参数就是我们要读出几个字符,最后一个参数就是要从哪个流中读取数据。

如果读取成功,那么其返回值就是从文件中取出的第一行的字符串的首地址

可以使用%s打印出来,如果读取失败,那么则会返回一个空指针NULL

下面我们使用这个函数看看:

运行结果:

4、fputs函数

fgets函数和fputc函数有点像,不过这个函数是将一行内容写入文件中

原型:

int fputs(const char * str,FILE*stream);

这个函数的第一个参数就是我们要写入的字符串,第二个参数就是要写入的流

然后如果文件写入成功,那么就返回一个非零的值,如何写入失败就返回EOF

下面我们使用这个函数进行写入操作:

运行后我们看看这个文件的内容:

可以看到这个文件的内容变成了我们传入的字符串。

5、fscanf函数

fscanf是以格式化的方式对文件进行读取操作,我们可以发现其和我们一个函数很像,其和scanf函数的使用方法是类似的,它们的区别就是fcanf的第一个参数是流,后面和scanf的参数是一样的,我们来对比一下这两个函数的原型:

int scanf(const char * format,...);

int fscanf(FILE*stream,const char * format,....);

可以看到它们的区别就是fscanf函数多了一个流的选择,其返回值也是一样的,返回值都是返回成功读取的项目的个数,如果读取失败的话,那么就返回EOF。

scanf函数是从标准输入流读取数据,而fscanf函数可以从任何流中读取数据,那么fscanf也必然是可以从标准输入流读取数据的,那么此时这两个函数其实是一致的,我们在上面也说过标准输入流是stdin,那么我们将fsacnf函数的第一个参数写成标准输入流stdin就可以了。

下面我们使用这个函数看看:

使用这个函数前,我们先使得环境达到这个条件:

当前文件夹中有一个test.txt文件,里面包含的数据有:123hello,现在我们要以格式化的方式将它们读取出来,也就是将123读取为整型,hello读取为字符串。

如下:

运行结果:

6、fprintf函数

可以发现这个函数和printf函数非常相似,其和printf函数一样,也是第一个参数不一样,其他的使用方法都是一样的。

其原型如下:

int fprintf(FILE*stream,const char *format,....);

它的返回值就一样,也是成功的个数。

不过其作用是不一样的,fprintf函数的作用是向所有流中写入数据,而printf是向标准输出流写入数据,fprintf要全面一点。

当fprintf的第一个参数是stdout的时候,那么其作用就和printf是一样的了。

下面我们使用其对文件进行写入一些格式化的数据:

运行后文件变化:

7、fwrite函数

这个函数和上面的几个函数有很大的区别,上面的函数对文件的输入输出都是文本的形式,而这个函数是以二进制的形式进行。

所以这个函数是对于二进制文件进行操作的,所以我们打开文件的时候,也要使用rd或者wd的方式打开。

这个函数是向文件中写入二进制的信息。

原型:

size_t fwrite(const void * ptr , size_t size , size_t count ,FILE*stream);

它的第一个参数是我们要写入的信息的首地址,第二个参数size是我们要写入的信息的一个元素的大小,第三个元素count就是我们要写入的元素的个数,最后一个参数是我们要往那个流中去写入信息。

它的返回值就是被成功写入到文件中的元素的个数。

下面我们使用这个函数为文件写入一些二进制的信息。

代码如下:

我们运行这个代码后,再打开这个文件看看其变化:

可以看到这个文件中写入了一些二进制的东西,但是我们打开这个文件是看不出这个文件存放的具体是上啥信息。那么我们下面学习另外一个函数。

8、fread函数

fread函数的作用就是从文件中读取二进制的信息的,其可以搭配上面的fwrite函数来使用。'

原型:

size_t fread(void * prt,suze_t size,size_t count ,FILE*stream);

这个函数的参数和返回值类型和fwite函数是一样的。其参数和返回值的意思是一样的。

在前面我们使用了fwrite函数对test.txt文件写入了整型1~5,那么我们使用这个函数对其进行读取,验证这个写入是否正确。

代码如下:

运行结果:

使用这个函数就可以判断我们的fwrite函数对文件的写入是否正确了。

相关推荐
我不会编程55516 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
李少兄16 小时前
Unirest:优雅的Java HTTP客户端库
java·开发语言·http
无名之逆16 小时前
Rust 开发提效神器:lombok-macros 宏库
服务器·开发语言·前端·数据库·后端·python·rust
啊喜拔牙17 小时前
1. hadoop 集群的常用命令
java·大数据·开发语言·python·scala
xixixin_17 小时前
为什么 js 对象中引用本地图片需要写 require 或 import
开发语言·前端·javascript
W_chuanqi17 小时前
安装 Microsoft Visual C++ Build Tools
开发语言·c++·microsoft
anlogic17 小时前
Java基础 4.3
java·开发语言
蒙奇D索大18 小时前
【数据结构】第六章启航:图论入门——从零掌握有向图、无向图与简单图
c语言·数据结构·考研·改行学it
A旧城以西18 小时前
数据结构(JAVA)单向,双向链表
java·开发语言·数据结构·学习·链表·intellij-idea·idea