椋鸟C语言笔记#32

文件、文件指针(流)、文件的打开与关闭

Featured image

萌新的学习笔记,写错了恳请斧正。

文件标识(文件名)

每个文件都有一个唯一的文件标识,也就是文件名

文件名包含 3 个部分:文件路径 + 文件名主干 + 文件后缀(可省略)

例如:C:\Windows\WindowsUpdate.log

我们常说的 “文件名” 是指文件名主干

文件分类

文件分为程序文件数据文件

其中数据文件根据数据的组织形式分为文本文件二进制文件

文本文件以 ASCII 字符的形式存储,二进制文件就是以二进制存储

流和标准流的基本概念

程序往往需要输出到各种外部设备,也需要从外部设备获取数据。为了方便程序员对各种不同设备进行输入输出的调控,就抽象出了流的概念。流可以大概理解为数据传递的一个过程

一般情况下,程序想要输入或输出数据都要先打开对应设备的流,然后在操作

那么为什么我们从键盘上输入数据,向控制台(屏幕)上输出数据时,不需要打开流呢?

这就是因为 C 语言程序启动时就会默认打开 3 个流,我们称之为标准流

标准流

这三个流默认打开,其类型为 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

注意:

在更新模式(存在标签 “+”)下打开文件时:

(这部分相关的内容后面讲,涉及到缓冲区、文件定位函数等知识。暂且先列在这里,原因后面缓冲区那里再说。)

文件打开的模式与其返回的文件指针的权限息息相关,比如 “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;
}