2 min to read
椋鸟C语言笔记#32
文件、文件指针(流)、文件的打开与关闭
萌新的学习笔记,写错了恳请斧正。
文件标识(文件名)
每个文件都有一个唯一的文件标识,也就是文件名
文件名包含 3 个部分:文件路径 + 文件名主干 + 文件后缀(可省略)
例如:C:\Windows\WindowsUpdate.log
我们常说的 “文件名” 是指文件名主干
文件分类
文件分为程序文件和数据文件
其中数据文件根据数据的组织形式分为文本文件和二进制文件
文本文件以 ASCII 字符的形式存储,二进制文件就是以二进制存储
流和标准流的基本概念
流
程序往往需要输出到各种外部设备,也需要从外部设备获取数据。为了方便程序员对各种不同设备进行输入输出的调控,就抽象出了流的概念。流可以大概理解为数据传递的一个过程
一般情况下,程序想要输入或输出数据都要先打开对应设备的流,然后在操作
那么为什么我们从键盘上输入数据,向控制台(屏幕)上输出数据时,不需要打开流呢?
这就是因为 C 语言程序启动时就会默认打开 3 个流,我们称之为标准流
标准流
- stdin:标准输入流,大多数环境从键盘输入,scanf 函数就是从 stdin 读取数据
- stdout:标准输出流,大多数环境输出到控制台,printf 函数就是输出数据到 stdout
- stderr:标准错误流,大多数环境输出到显示器界面
这三个流默认打开,其类型为 FILE*,通常也称为文件指针
(为什么流会有类型呢?这就是下面要讲的重点了)
在 C 语言中,我们通过文件指针来维护各种流的操作
文件指针(C 语言中流的实质)
ANSIC 标准采用 “缓冲文件系统” 来处理各种数据文件,其中 “文件类型指针”(简称 “文件指针”)是一个非常重要的概念。
每一个被使用的文件都会在内存中开辟一个相应的文件信息区,用于存放文件的相关信息(文件名主体、状态、路径等等),这些信息被保存在一个结构体变量中,而这个由系统声明的结构体类型就是 FILE,比方说某环境下 stdio.h 中就有这样的声明(不同环境下大同小异):
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
程序每当打开一个文件,系统就根据文件的情况创建一个 FILE 类型的结构体变量,并填充其中的信息。而一般我们使用一个 FILE * 类型的指针来维护这个 FILE 类型的结构体变量,而这就是我们实现程序与文件之间通讯的基础,也就是 C 语言中流的实质
文件指针变量可以指向某个文件的文件信息区(一个结构体变量),这样我们就可以通过文件信息区的信息访问和操作文件
文件的打开和关闭
文件在读写前应该先打开,在使用结束后应该关闭
ANSIC 规定 fopen 函数用于打开文件,fclose 函数用来关闭文件,原型如下:
#include <stdio.h>
FILE* fopen( const char* filename, const char* mode );
int fclose( FILE* stream );
fclose
fclose 函数就是关闭 stream 指针指向的文件,成功则返回 0,失败则返回 EOF
关闭文件的实质是:将已经写到输入缓冲区的信息冲入、已经写到输出缓冲区的信息冲出;将尚未写入缓冲区的信息舍弃;最后取消该文件指针与文件的关联
(前两步其实就是刷新缓冲区,缓冲区相关知识下面会讲)
fopen
fopen 函数就是以 mode 模式打开 filename 指向的文件,并返回一个指向该文件的 FILE * 指针
如果打开文件失败则返回空指针 NULL 并设置对应的 errno
filename 如果包含路径,则会寻找到对应的路径;如果不包含路径,就直接在本目录下(在 Windows 下表现为在本文件夹下)打开
mode 称为文件访问模式字符串,其值与文件打开模式的对应关系如下:
文件访问模式字符串 | 含义 | 解释 | 若文件已存在 | 若文件不存在 |
---|---|---|---|---|
"r" | 只读 | 打开文件以读取 | 从头开始读 | 打开失败报错 |
"w" | 只写 | 创建文件以写入 | 销毁内容重写 | 创建新文件 |
"a" | 只后附 | 后附到文件 | 从结尾开始写 | 创建新文件 |
"r+" | 读扩展 | 打开文件读写 | 从头开始读写 | 打开失败报错 |
"w+" | 写扩展 | 创建文件读写 | 销毁内容重新读写 | 创建新文件 |
"a+" | 后附扩展 | 打开文件读写 | 从头开始读 从结尾开始写 | 创建新文件 |
标签 b:在文件访问模式字符串后加一个 b 则代表以二进制模式进行(只有 Windows 下),b 是和 + 类似的后缀所以顺序不影响。比如说:"wb+" 和 "w+b" 都代表创建二进制文件读写,其行为与 "w+" 也是一样的。 | ||||
标签 t:其实如果没有特意去加标签 b,就默认后面有标签 t,代表以文本的模式进行。比方说 "a+" 其实就是 "a+t" 或者 "at+" | ||||
后缀 x:在基于 "w" 的文件访问模式字符串的结尾加上 x 就代表如果文件已经存在的话,不再销毁内容重新写 / 读写,而是直接认为函数失败并返回 NULL |
注意:
在更新模式(存在标签 “+”)下打开文件时:
- 若中间没有 fflush 函数或文件定位函数,则输出后不应有输入
- 若中间没有文件定位函数且输入操作没有遇到文件尾,则输入后不应有输出
(这部分相关的内容后面讲,涉及到缓冲区、文件定位函数等知识。暂且先列在这里,原因后面缓冲区那里再说。)
文件打开的模式与其返回的文件指针的权限息息相关,比如 “w” 和 “a” 模式下文件指针是无效的;”w+” 和 “a+” 模式下文件指针只对读有效,如果写,文件指针就会自动移到文件尾
举个栗子
下面就是在本目录下创建一个 abc.txt 并写入字母表的程序:
#include <stdio.h>
int main()
{
FILE* pf = fopen("abc.txt", "w");
if (pf == NULL)
{
perror(fopen);
return 1;
}
for (int i = 0; i < 26; i++)
fputc('a' + i, pf);
fclose(pf);
pf = NULL;
return 0;
}
Comments