C语言KR圣经笔记 7.5文件访问 7.6错误处理-stderr和exit

7.5 文件访问

到目前为止,给出的例子都是读标准输入和写标准输出,而标准输入和输出都是由本地操作系统自动为程序定义的。

下一步是写个程序来访问还没有被连接到程序上的文件。我们以一个叫做 cat 的程序来说明这种操作需求,它连接一系列指定的文件并打印到标准输出。cat 用来在屏幕上打印文件,它还能用作通用的输入收集器,供那些没有能力通过名字来访问文件的程序所使用。例如下面的命令行

cat x.c y.c

在标准输出上打印文件 x.c 和 y.c 的内容(没有其他东西)。

问题在于如何安排这些指定要读的文件——即如何把用户想到的一个外部名字和读数据的语句关联起来。

规则是简单的。在文件能被读写之前,它必须用库函数 fopen 来“打开”。fopen 获取一个如 x.c 或 y.c 这样的外部名称,做一些杂活,并和操作系统协商(细节我们不关心),然后返回一个后续可以用来对文件进行读或写操作的指针。

这个指针,称为文件指针,指向一个包含文件信息的结构体,例如:缓存的位置,缓存中当前字符的位置,文件是被还是写,是否遇到了错误或者文件结束,等等。用户不需要知道细节,因为从<stdio.h> 中获取的定义包含了一个称为 FILE 的结构体。而使用文件指针,只需要如下声明:

FILE *fp;
FILE *fopen(char *name, char *mode);

这里说 fp 是指向 FILE 的指针,而 fopen 返回一个指向 FILE 的指针。注意 FILE 是类型名,就像int 一样,而不是结构体标签;它是用 typedef 定义的。(关于如何在 UNIX 系统上实现 fopen,详见 8.5 节)

程序里调用 fopen 的方式如下:

fp = fopen(name, open);


fopen 的第一个参数是包含文件名的字符串指针。第二个参数也是一个字符串指针,叫“模式”,表示我们想要如何使用这个文件。允许的模式包括读("r"),写("w")和追加("a")。某些系统区分文本文件和二进制文件;对于后者,必须在模式字符串后面加上一个 "b"。


如果模式为“写”或“追加”时文件不存在,则(若可能的话)会创建这个文件。以写模式打开一个已存在的文件,会使其丢弃旧的内容,而用追加模式,则会保留旧内容。试图读不存在的文件是错误的,另外还可能存在其他错误,例如试图读你无权访问的文件。如果有任何错误,fopen 会返回 NULL。(可以更精确地标识错误,参见附录 B 第一节末尾讨论的错误处理函数)

一旦文件被打开,下一件需要的事是读或写文件的方式。有几种可能的方式,其中 getc 和putc 是最简单的。getc 返回文件的下一个字符;需要文件指针来告诉它读的是哪个文件。

int getc(FILE *fp)

getc 从通过 fp 所指的流中返回下一个字符;如果文件结尾或错误,则返回 EOF。

putc 是输出函数

int putc(int c, FILE *fp)

putc 把字符 c 写到通过 fp 所指的流中,并返回所写的字符,如果遇到错误则返回 EOF。像 getchar 和 putchar 一样,getc 和 putc 也可能是宏而不是函数。

当一个 C 程序启动时,操作系统环境负责打开三个文件并提供其文件指针。这三个文件是:标准输入、标准输出和标准错误;对应的文件指针称为 stdin、stdout 和 stderr,均在 <stdio.h> 中声明。通常 stdin 连接到键盘,而 stdout 和 stderr 连接到屏幕,不过 stdin 和 stdout 可以被重定向到文件或管道,如7.1节所述。

getchar 和 putchar 可以用 getc、putc、stdin、stdout 来定义,如下:

#define getchar()     getc(stdin)
#define putchar(c)    putc((c), stdout)

对文件的格式化输入和输出,可以使用 fscanf 和 fprintf。它们与 scanf 和 printf 完全一样,仅仅区别在第一个参数是要读或写的文件指针,第二个参数才是格式化字符串。

int fscanf(FILE *fp, char *format, ...)
int fprintf(FILE *fp, char *format, ...)

有了这些预备知识之后,我们现在就能够写出用来连接文件的 cat 程序了。我们采用的是大家发现对很多程序来说都很方便的一个设计。如果有命令行参数,则将它们解释为文件名,并依次处理。如果没有,则处理标准输入。

#include <stdio.h>/* cat:连接多个文件,第一版 */
main(int argc, char *argv[])
{FILE *fp;void filecopy(FILE *, FILE *);if (argc == 1)    /* 无参数,拷贝标准输入 */filecopy(stdin, stdout);elsewhile (--argc > 0) {if ((fp = fopen(*++argv, "r") == NULL) {printf("cat: can't open %s\n", *argv);return 1;} else {filecopy(fp, stdout);fclose(fp);}}return 0;
}
/* filecopy: 把文件ifp 拷贝到文件 ofp */
void filecopy(FILE *ifp, FILE *ofp)
{int c;while ((c = getc(ifp)) != EOF)putc(c, ofp);
}

文件指针 stdin 和 stdout 是 FILE * 类型的对象。然而,它们是常量而不是变量,因此不可能对它们赋值。

函数 flose 

int fclose(FILE *fp)

是 fopen 的反面;它打断了由 fopen 建立的文件指针和外部名称的关联,并将文件指针释放给其他文件使用。由于大部分操作系统对一个程序可以同时打开的文件数量有限制,当不再需要时将文件指针释放是一个好主意,正如我们在 cat 中所做的。对输出文件执行 fclose 还有一个原因——它清空(flush)了 putc 所收集的输出缓存。当程序正确结束时,fclose 会被自动调用来关闭每个打开的文件。(如果不需要的话,你可以关闭标准输入和输出。也可以用库函数 freopen 对它们进行重新分配。)


 

7.6 错误处理——stderr 和 exit

cat 中的错误处理不够理想。麻烦在于,如果其中一个文件由于某些原因无法访问,则会在连接输出的末尾打印出诊断信息。如果输出到屏幕,这也许可以接受,但如果输出到文件,或者通过管道输出到另一个程序,就无法接受了。

为了更好地处理这种情况,第二个输出流,称为 stderr,以和 stdin 和 stdout 同样的方式被分配给程序。即使标准输出被重定向了,写到 stderr 的输出也通常显示在屏幕上。

我们来修订 cat ,使其错误信息写到标准输出。

#include <stdio.h>/* cat: 连接多个文件,第二版 */
main(int argc, char *argv)
{FILE *fp;void filecopy(FILE *, FILE *);char *prog = argv[0];        /* 程序名称,用于错误输出 */if (argc == 1)        /* 无参,使用标准输入 */filecopy(stdin, stdout);elsewhile (--argc > 0)if ((fp = fopen(*++argv, "r") == NULL) {fprintf(stderr, "%s: can't open %s\n", prog, *argv);exit(1);} else {filecopy(fp, stdout);fclose(fp);}if (ferror(stdout)) {fprintf(stderr, "%s: error writing output\n", prog);exit(2);}exit(0);
}

程序以两种方式来指示错误。第一,fprintf 产生的诊断输出发到 stderr,因此会输出到屏幕上,而不是消失到管道中或进入输出文件中。我们在错误消息中包含了来自 argv[0] 都程序名,这样如果程序和其他程序一起使用时,就能识别出错误的源头。

第二,程序使用了标准库函数 exit,当它被调用时会终止程序执行。exit 的参数可以提供给任何调用当前进程的进程,因此把本程序作为子进程的另一个程序可以判断本程序是成功或失败。根据惯例,返回值 0 表示一切正常;非 0 通常表示不正常的情况。exit 会调用 fclose 来关闭每个输出文件,以清空(flush)任何缓存的输出。

在 main 函数中, return expr 等价于 exit(expr)。exit 的优势是它能被 main 之外的其他函数调用,比如可以在第五章的样式搜索程序中找到对 exit 的调用。

如果在流 fp 上出现错误,ferror 函数返回非零。

int ferror(FILE *fp)

尽管输出错误比较少见,但的确也会发生(例如磁盘满),因此用于生产的程序也应当进行检查。

函数 feof(FILE *) 类似 ferror,如果在指定的文件上发生了文件结束,则返回非零。

在这些用于演示的小程序中,我们通常不关心返回状态,但所有认真(serious)的程序都应该返回合理、有用的状态值。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/689598.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统测试计划(直接套用实际)

1测试目的 2测试范围 3资源要求 3.1人力资源 3.2指派干系人 3.3测试环境 3.4测试工具 4测试类型 5测试安排 5.1测试进度 5.2测试策略 5.2.1测试需求 5.2.2测试类型 6测试停止标准 7测试风险 8缺陷管理 8.1缺陷属性 8.2缺陷类型 8.3缺陷严重程度 8.4缺陷优先级 8.5缺陷状态 8.6缺…

【Java万花筒】跨越云平台的无服务器开发:使用Java构建弹性、高效的应用

无服务器计算平台的Java集成指南&#xff1a;AWS Lambda、Google Cloud Functions、腾讯云函数和IBM Cloud Functions 前言 无服务器计算平台提供了一种方便、弹性和成本效益高的方式来运行代码&#xff0c;而无需关心底层基础设施的管理。在这篇文章中&#xff0c;我们将探讨…

C#上位机与三菱PLC的通信07--使用第3方通讯库读写数据

1、通讯库介绍 mcprotocol 是一个基于 Node.js 的三菱 PLC MC 协议通信库&#xff0c;具有以下特点&#xff1a; 支持多种三菱 PLC MC 协议的设备&#xff0c;如 FX3U、Q03UDECPU、QJ71E71 等。 支持多种功能码和数据类型&#xff0c;如读取线圈&#xff08;M&#xff09;、…

[AIGC] 利用 chatgpt 深入理解 Java 虚拟机(JVM)

Java 虚拟机&#xff08;JVM&#xff09;是 Java 编程语言的核心运行环境&#xff0c;它负责解释和执行 Java 字节码。它是 Java 程序能够跨平台运行的关键&#xff0c;因为不同的操作系统和硬件平台都有自己的指令集和体系结构&#xff0c;而 JVM 则提供了一个统一的运行环境&…

C#面:列举ASP.NET页面之间传递值的几种方式

查询字符串&#xff08;Query String&#xff09;&#xff1a; 可以通过在URL中添加参数来传递值。 例如&#xff1a;http://example.com/page.aspx?id123 在接收页面中可以通过Request.QueryString[“id”]来获取传递的值。 会话状态&#xff08;Session State&#xff0…

com.google.android.material.tabs.TabLayout

一、布局 <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"xmlns:tools"http://schemas.android.com/tools"android:layout_width"match_parent"a…

P13 进制

进制本质&#xff0c;每一个数位上的数字乘上这一位的权重 再计算机中&#xff0c;数字均通过二进制补码表示 #include <bits/stdc.h> using namespace std; using ll long long; const int N 50; int a[N] ; int main() {string s "2021ABCD";for(int i…

LeetCode 每日一题 2024/2/12-2024/2/18

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 2/12 145. 二叉树的后序遍历2/13 987. 二叉树的垂序遍历2/14 102. 二叉树的层序遍历2/15 107. 二叉树的层序遍历 II2/16 103. 二叉树的锯齿形层序遍历2/17 429. N 叉树的层…

硬核!10分钟教你搭建一个本地版GPT4.0!

今天10分钟手把手教会你在自己电脑上搭建一个官方原版的GPT4.0。 不用ChatGPT账号&#xff0c;不用API&#xff0c;直接免费使用上官方原版的GPT4.0&#xff01; 对&#xff01;你没看错&#xff01;不仅是正版GPT4.0&#xff0c;还完全免费&#xff01; 而且整个部署流程极其简…

Pytorch的常用模块和用途说明

关注B站可以观看更多实战教学视频&#xff1a;肆十二-的个人空间-肆十二-个人主页-哔哩哔哩视频 (bilibili.com) Hi&#xff0c;兄弟们&#xff0c;这里是肆十二&#xff0c;今天我们来讨论一下深Pytorch中的常用模块。 PyTorch是一个开源的深度学习平台&#xff0c;提供了许多…

java根据前端所要格式返回树形3级层级数据

一、业务分析&#xff0c;根据前端需求返回如下数据格式 二、后端设计数据类型VO /*** author TTc* version 1.0* date 2024/2/15 16:47*/ Data AllArgsConstructor NoArgsConstructor public class Catalog2Vo {/*** 一级父分类的 id*/private String catalog1Id;/*** 三级子…

C# 12 中新增的八大功能你都知道吗?

一、主构造函数 在 Visual Studio 2022 版本 17.6 预览版 2 中引入。 从 C# 12 开始&#xff0c;可以在类和结构中声明主构造函数。主构造函数参数都在类的整个主体的范围内。为了确保显式分配所有主构造函数参数&#xff0c;所有显式声明的构造函数都必须使用 this() 语法调用…

SSL证书要钱吗?SSL证书一定要安装吗?

为了保护网站的数据安全和用户隐私&#xff0c;越来越多的网站开始采用SSL证书来加密数据传输。那么&#xff0c;SSL证书到底是否需要收费呢&#xff1f;又是否一定要安装呢&#xff1f;本文将从专业角度为您解答这些问题。 首先&#xff0c;我们来了解一下什么是SSL证书。SSL…

怎样解决恢复VPS数据信息?

对于服务器来说其中的数据信息是十分重要的内容&#xff0c;一旦出现数据丢失或损坏&#xff0c;就会对企业造成巨大的损失&#xff0c;所以备份是非常重要的&#xff0c;那么在使用VPS时我们怎样能够恢复其中的数据信息呢&#xff1f; 一、手动备份 其中比较简单快速的备份方…

CPU是如何工作的?什么是冯·诺依曼架构和哈弗架构?

《嵌入式工程师自我修养/C语言》系列——CPU是如何工作的&#xff1f;什么是冯诺依曼架构和哈弗架构&#xff1f; 一、CPU内部结构及工作原理1.1 CPU的结构1.2 CPU工作流程举例 二、计算机体系结构2.1 冯诺依曼架构2.2 哈弗架构 三、总结 快速学习嵌入式开发其他基础知识&#…

《源代码》:穿越思考的时空之旅

计算机专业必看的几部电影 计算机专业必看的几部电影&#xff0c;就像一场精彩的编程盛宴&#xff01;《黑客帝国》让你穿越虚拟世界&#xff0c;感受高科技的魅力&#xff1b;《社交网络》揭示了互联网巨头的创业之路&#xff0c;《源代码》带你穿越时间解救世界&#xff0c;…

error: src refspec main does not match any解决办法

一、问题描述&#xff1a; 用GitHub Actions自动部署Hexo&#xff0c;到了最关键的一步&#xff1b;突然报错&#xff1a;error: src refspec main does not match any 1、错误一&#xff1a; main分支应填写为master分支&#xff1b;但是只改这里也会报其他错误 2、错误二&a…

mqtt 协议的概念和理解

一、概述 MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的”轻量级”通讯协议&#xff0c;该协议构建于TCP/IP协议上&#xff0c;由IBM在1…

统计zabbix指定日期内的告警数量

问题描述&#xff1a; 知名企业A公司的运维人员小智,需要对zabbix发生的告警数量进行统计。 解决方案&#xff1a; 1、数据库查询方案&#xff0c;调整时间范围即可查询告警相应数据&#xff1a; 查询最近30天zabbix告警数据 SELECTa.hostid ,a.host,a.name AS hostnname,b.na…

【C项目】无头单向不循环链表

简介&#xff1a;本系列博客为C项目系列内容&#xff0c;通过代码来具体实现某个经典简单项目 适宜人群&#xff1a;已大体了解C语法同学 作者留言&#xff1a;本博客相关内容如需转载请注明出处&#xff0c;本人学疏才浅&#xff0c;难免存在些许错误&#xff0c;望留言指正 作…