为什么程序需要文件
之前的程序把输入保存在变量或数组中。这些数据位于程序运行时使用的内存里;程序结束后,下次运行并不会自动恢复上次的变量值。如果希望保存成绩、订单或设置,供以后再次使用,就需要把数据写到文件中。
文件是由操作系统管理、通常保存在磁盘等存储设备上的一组数据。文件名和路径帮助我们找到它。文本文件中的数据可以按字符来处理,例如一行姓名和成绩;程序也可以读取这些字符,而不必依赖人把内容重新输入一遍。
程序“打开文件”,不是打开一个窗口
在桌面上双击文件,通常是让编辑器或其他应用显示它。而 C 程序调用 fopen,不会弹出文件窗口:它是根据路径和打开模式,请求建立一条可以读写文件的通道。
可以把过程理解为:先找到文件并取得访问通道,再通过通道读取或写入数据,最后结束这次访问。打开成功不等于已经读取了全部内容,也不等于把整个文件复制进了某个变量。
这条通道在 C 中叫作文件流(stream)。标准库用 FILE 类型管理流的状态,例如当前位置、读写状态和缓冲信息;fopen 返回的 FILE * 指向这个管理对象。它不是文件内容的首地址,不能通过解引用它来取得文件里的字符。
先打开、检查,再关闭
fopen("file-io-sample.txt", "r") 的第一个参数告诉程序去哪里找文件,第二个参数 "r" 表示本次只读取已有文件。成功时返回流指针;路径不存在、权限不足等情况都可能导致失败,此时返回 NULL。必须先检查,不能把失败得到的空指针交给后续读写或关闭函数。
下面先只练习建立通道和结束访问,暂时不读取文件内容。
#include <stdio.h>
int main(void) {
FILE *fp = fopen("file-io-sample.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Cannot open file-io-sample.txt.\n");
return 1;
}
if (fclose(fp) != 0) {
fprintf(stderr, "Cannot close the file.\n");
return 1;
}
return 0;
}fprintf(stderr, "..."); 把指定的提示文本写到标准错误流。先把它理解为“给用户显示错误信息”即可:stderr 由 <stdio.h> 提供,通常也显示在终端里,不需要我们自己打开或关闭。本例的提示由程序员编写,并不自动解释操作系统的具体错误原因。
fclose(fp) 结束程序通过这个流进行的文件访问,并释放相关资源;如果流里有尚未提交的输出缓冲数据,也会尝试将其写出。它不是关闭屏幕上的窗口,更不会删除文件。这里以只读模式打开,所以没有待写出的内容。
fclose 返回 0 表示关闭成功,非 0 表示发生错误。调用后不能再用这个流指针进行读写,也不要再次关闭它;即使返回了错误,也应报告错误,而不是重复调用。以后需要继续访问这个文件,可以重新调用 fopen,获得一个新的流。
准备示例文件
路径相对于程序启动时的当前工作目录,而不一定相对于源代码文件。运行示例前,请复制下面的文本,在该目录中自行创建 file-io-sample.txt:
Leaflet file I/O sample
The second line is ready to read.文本文件打开模式
| 模式 | 读取 | 写入 | 文件不存在 | 文件已存在时 | 初始位置 |
|---|---|---|---|---|---|
r | 是 | 否 | 失败 | 保留 | 开头 |
w | 否 | 是 | 创建 | 清空 | 开头 |
a | 否 | 是 | 创建 | 保留;写入追加 | 由实现决定在开头或末尾;写入始终在末尾 |
r+ | 是 | 是 | 失败 | 保留 | 开头 |
w+ | 是 | 是 | 创建 | 清空 | 开头 |
a+ | 是 | 是 | 创建 | 保留;写入追加 | 读取位置由实现规定,写入在末尾 |
w 和 w+ 会立即清空已有文件。模式中的 + 表示同时允许读写,不代表“追加”。
知识测验
知识测验
知识测验
安全检查并关闭示例文件
复制本节给出的文本并自行创建 file-io-sample.txt,然后编写程序以 r 模式打开它。打开失败时用 fprintf(stderr, ...) 输出错误提示并返回 1;成功时输出“opened”,检查 fclose 的结果后返回 0。本题暂不读取文件内容。
- 调用 fopen("file-io-sample.txt", "r")。
- 返回 NULL 时输出错误并立即结束。
- 成功时完成提示,然后检查 fclose 是否返回 0。