【C语言加油站】C语言文件操作详解:从“流”的概念到文件的打开与关闭
文件操作
- 导读
- 一、流与标准流
- 1.1 流
- 1.2 标准流
- 二、文件指针
- 三、文件的打开与关闭
- 3.1 `fopen()`
- 3.2 `fclose()`
- 3.3 函数的使用
- 结语
导读
大家好,很高兴又和大家见面啦!
在之前的文章中,我们已经对“文件”有了比较全面的认识:
- 文件是存储在计算机上的信息集合,是程序与数据持久化的重要载体。
我们了解了文件的基本操作,如打开、读取、写入和关闭,也区分了文本文件与二进制文件的本质不同:
- 文本文件以字符编码(如ASCII)方式存储,便于人类直接阅读
- 二进制文件则保留数据的原始格式,更适合机器高效处理
理解了“什么是文件”以及“文件如何存储”之后,我们自然会问:在C语言中,如何具体实现文件的操作?
这就涉及到一个核心概念——“流”(Stream),以及与之相关的文件指针和标准流机制。通过“流”,C语言将不同的输入输出设备(如键盘、屏幕、磁盘文件)抽象为统一的数据通道,使得文件操作变得灵活而一致。
现在,就让我们一起深入探讨C语言中的“流”与文件操作的具体实现。
一、流与标准流
1.1 流
流是一个内涵非常丰富的概念,它既指液体或气体这类物质的移动,也是计算机科学中用于抽象描述数据序列或传输过程的重要模型。
在物理世界中,“流”最直观的理解就是液体或气体的移动。例如,河水的流动、空气的流动(风)都是一种流。流体力学就是研究流体运动规律的科学。其核心原理包括:
- 连续性:流体被视为连续介质,其物理量在空间中连续变化。
- 能量守恒:以伯努利方程为例,它描述了在不可压缩流体的稳定流动中,速度、压强和高度之间的关系:动能、压力势能和位能之和在流线上保持不变。
在计算机科学中,流(Stream) 是一个非常重要且基础的抽象概念,它核心是数据处理的一种方式。它主要用来处理一系列有序的数据元素。
如何理解这个抽象概念呢?
在成长的过程中,我相信大家都有听过一句话——我们遨游在知识的海洋中。
在这句话中,我们就把所学习的各种知识给具象化成了组成海洋的海水,由这些海水汇聚而形成的海洋,我们就将其称之为知识的海洋。
同样的,在计算机邻域中,我们把计算机中的各种数据具象化成河水,这些河水会汇聚成一条河流,不断地在计算机世界中进行流动。
我们可以向这条河流中倒入新的河水(数据),同样的我们也可以从这条河流中取出我们需要的河水(数据)。
但是有一点需要注意:我们不管是倒入新的河水,还是取出我们需要的河水,我们首先需要做的就是找到这条河流,即,我们在进行输入数据和输出数据前,需要先打开流。
1.2 标准流
那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语⾔程序在启动的时候,默认打开了3个流:
stdin
标准输⼊流:在⼤多数的环境中从键盘输⼊,scanf
函数就是从标准输⼊流中读取数据。stdout
标准输出流:⼤多数的环境中输出⾄显示器界⾯,printf
函数就是将信息输出到标准输出流中stderr
标准错误流:⼤多数环境中输出到显示器界⾯。
这是默认打开了这三个流,我们使⽤scanf
、printf
等函数就可以直接进⾏输⼊输出操作的。
stdin
、stdout
、stderr
三个流的类型是: FILE*
,通常称为文件指针。
C语⾔中,就是通过 FILE* 的文件指针来维护流的各种操作的。
二、文件指针
缓冲⽂件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使⽤的文件都在内存中开辟了⼀个相应的文件信息区,⽤来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名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*
的指针变量:
FILE* pf = NULL; // 创建文件指针变量// pf——point file:指向文件的指针
这里我们定义了一个指向 FILE
类型数据的指针变量 pf
。
可以使 pf
指向某个文件的文件信息区(⼀个结构体变量)。
通过该⽂件信息区中的信息就能够访问该⽂件。
也就是说,通过文件指针变量能够间接找到与它关联的⽂件。
三、文件的打开与关闭
在前面的内容中我们就有介绍过,我们可以对文件执行的基本操作有:创建、销毁、读取、写入……
-
在我们完成了对文件的创建后,如果我们想要对该文件进行内容的读取和写入操作,那我们首先需要做的就是打开该文件;
-
当我们在使用完文件,甚至想要销毁文件时,我们则需要先关闭该文件
因此我们首先需要学习的基本操作就是文件的打开与关闭。
在C语言的 stdio.h
这个头文件中,有一系列专门用来进行文件操作的库函数:
fclose()
——用于关闭一个已经打开的流(文件)fflush()
——用于强制将输出流或更新(最近操作为输出)缓冲区中的数据立即写入文件fopen()
——用于使用指定模式打开一个文件,并关联一个流freopen()
——用于将已有的流重新关联到另一个文件setbuf()
——用于为指定的流设置自定义缓冲区或禁用缓冲setvbuf()
——用于为指定的流设置缓冲模式(全缓冲、行缓冲、无缓冲)和缓冲区
下面我们主要介绍两个函数——fopen
与 fclose
3.1 fopen()
在 cplusplus.com 网站中有对该函数的详细介绍,有兴趣的朋友可以点击链接进行访问。这里我们对该函数进行一下使用说明:
从上述的函数说明中我们可看到函数的一个大致用法:
- 向函数中传入两个参数:要操作的文件名以及对文件的操作模式
那么该函数能够对文件进行哪些具体的操作呢?
简单来说,核心操作有3类:
- 读文件:对指定的存在文件进行读操作,若文件不存在则会返回错误信息
- 写文件:对指定文件进行写操作,若文件不存在,则会创建一个新文件
- 添加数据:对指定文件的末尾追加数据,若文件不存在,则会创建一个新文件
在这三类核心操作中,我们可以细分为以下操作:
- 读文件:对指定的存在文件进行读操作,若文件不存在则会返回错误信息
r
:只读操作。为了输入数据,打开一个已经存在的文本文件rb
:只读操作。为了输入数据,打开一个二进制文件r+
:读写操作。为了读和写,打开一个文本文件rb+
:读写操作。为了读和写,打开一个二进制文件
- 写文件:对指定文件进行写操作,若文件不存在,则会创建一个新文件
w
:只写操作。为了输出数据,打开一个文本文件wb
:只写操作。为了输出数据,打开一个二进制文件w+
:读写操作。为了读和写,建立一个新的文件wb+
:读写操作。为了读和写,建立一个新的二进制文件
- 添加数据:对指定文件的末尾追加数据,若文件不存在,则会创建一个新文件
a
:追加操作。向文本文件末尾添加数据ab
:追加操作。向二进制文件末尾添加数据a+
:读写操作。在文件末尾进行读写ab+
:读写操作。在二进制文件末尾进行读写
fopen
这个函数对给定的操作是否成功,会有相应返回值:
- 文件打开成功:返回一个指向文件的指针
- 文件打开失败:返回一个空指针。在大多数的库实现中,如果失败了,也会把
errno
变量设置为系统特定的错误代码
3.2 fclose()
该函数的用法比较简单:
- 向函数中传入一个需要关闭的流
如果关闭成功,函数会返回 0
,如果关闭失败,函数会返回 EOF
。并且即使函数关闭失败,作为参数的 FILE*
指针指向的文件所关联的流也会与该文件和缓冲区断开关联。
3.3 函数的使用
了解了这两个函数的基本用法,下面我们就可以来尝试着使用一下这两个函数了:
void test1() {FILE* pf = NULL; // 创建文件指针变量// pf——point file:指向文件的指针// 这里我采用w+模式来创建一个名为:text.txt的文本文件,并进行读写pf = fopen("text.txt", "w+");// 当文件打开成功if (pf) {// 通过文件输入函数,向文件中写入:"fopen test"fputs("fopen test", pf);// 完成写入操作后,关闭流fclose(pf);}
}
下面我们就来测试一下:
此时程序并没有报错,那就说明我们成功创建了这个文件,并对其进行了写操作。由于我们并未对该文件的创建指定路径,因此文件会自动创建在与程序相同的文件夹下:
可以看到此时我们已经成功创建了这个文件,并且文件名为我们指定的命名。当我们打开该 .txt
文件后,我们就可以查看是否写入成功:
可以看到,文件中已经写入了我们需要写入的内容。
那我们如何指定文件的路径呢?
很简单,我们只需要在文件名的前面加上其所在路径即可:
从测试结果中我们可以看到,我们最开始通过仅读模式在指定路径中打开该文件时,由于不存在这个文件,因此返回空指针;
从 perror
的报错中我们也能获取相关信息,之后我们再一次通过仅写模式,成功在指定路径下创建了该文件,并将指定内容写入了文件内。
结语
今天的内容到这里就全部结束了。至此,我们已经对C语言文件操作的核心基础进行了一次深入的探索。让我们一同回顾本次旅程的要点:
-
核心概念“流”:我们理解了“流”(Stream)作为数据通道的强大抽象,它将多样的输入/输出设备统一起来,使得文件操作变得清晰而一致。
-
默认的“标准流”:我们认识了程序启动时自动打开的三个标准流——
stdin
(标准输入)、stdout
(标准输出)和stderr
(标准错误),这正是scanf
和printf
等函数能直接工作的原因。 -
关键钥匙“文件指针”:我们掌握了 FILE* 这个关键数据类型,它是指向文件信息区的指针,是所有文件操作的基础。
-
文件操作的基本功“打开与关闭”:我们重点学习了
fopen
和fclose
这一对必须成对使用的函数,详细了解了各种文件打开模式(如 r, w, a, r+ 等)的区别与适用场景,并成功进行了实践。
掌握文件的打开与关闭,只是迈入了文件处理世界的第一步。接下来,我们将学习如何具体地对文件进行读取和写入,探索更多强大的文件操作函数。
如果这篇博客帮助您理清了“流”与文件操作的基本概念,请不要忘记:
-
点赞 👍 - 您的认可是我持续创作的最大动力!
-
收藏 ⭐ - 方便日后随时查阅,巩固知识。
-
转发 ➤ - 分享给更多可能正在学习C语言的朋友,大家一起进步!
-
评论 💬 - 您有任何疑问、想法或指正,都欢迎在评论区留言,我们一起交流探讨!
期待在下一篇关于文件读写的博客中与您再次相遇!