参考文献:The C Programming Language
目录
7.5 文件访问
到目前为止,我们讨论的例子都是从标准输入读取数据,并向标准输出输出数据
标准输入和标准输出是操作系统自动提供给程序访问的
接下来,我们编写一个访问文件的程序,且它所访问的文件还没有连接到该程序
程序 cat 可以用来说明该问题,它把一批命名文件串联后输出到标准输出上
cat 可用来在屏幕上打印文件,对于那些无法通过名字访问文件的程序来说,它还可以用作通用的输入收集器
例如下列命令行:
cat x.c y.c
将在标准输出上打印文件 x.c 和 y.c 的内容
如何设计命名文件的读取过程呢?即如何将用户需要使用的文件的外部名同读取数据的语句关联起来
在读写一个文件之前,必须通过库函数 fopen 打开该文件
fopen 用类似于 x.c 或 y.c 这样的外部名与操作系统进行某些必要的连接和通信,并返回一个随后可以用于文件读写操作的指针
该指针称为文件指针,它指向一个包含文件信息的结构,这些信息包括:
缓冲区的位置、缓冲区中当前字符的位置、文件的读或写状态、是否出错或是否已经到达文件结尾等等
用户不必关心这些细节,因为
在程序中只需按照下列方式声明一个文件指针即可:
FILE *fp; FILE *fopen(char *name, char *mode);
在本例中,fp 是一个指向结构 FILE 的指针,并且,fopen 函数返回一个指向结构 FILE 的指针
FILE 是像 int 一样的类型名,不是结构标记,它是通过 typedef 定义的
UNIX 系统中 fopen 的实现细节将在 8.5 节中讨论
在程序中,可以这样调用 fopen 函数:
fp = fopen(name, mode);
fopen 的第一个参数是一个字符串,它包含文件名
第二个参数是访问模式,也是一个字符串,用于指定文件的使用方式,允许的模式包括:读("r")、写("w")及追加("a")
某些系统还区分文本文件和二进制文件,对后者的访问需要在模式字符串中增加字符 "b"
如果打开一个不存在的文件用于写或追加,该文件将被创建(如果可能的话)
当以写方式打开一个已存在的文件时,该文件原来的内容将被覆盖
如果以追加方式打开一个文件,则该文件原来的内容将保留不变
读一个不存在的文件会导致错误,其它一些操作也可能导致错误,比如试图读取一个无读取权限的文件
如果发生错误,fopen 将返回 NULL
可以更进一步地定位错误的类型,具体方法请参见附录 B.1 节中关于错误处理函数的讨论
文件被打开后,就需要考虑采用哪种方法对文件进行读写
有多种方法可供考虑,其中 getc 和 putc 函数最为简单
getc 从文件中返回下一个字符,它需要知道文件指针,以确定对哪个文件执行操作:
int getc(FILE *fp)
getc 函数返回 fp 指向的输入流中的下一个字符
如果到达文件尾或出现错误,该函数将返回 EOF
putc 是一个输出函数,如下所示:
int putc(int c, FILE *fp)
该函数将字符 c 写入到 fp 指向的文件中,并返回写入的字符
如果发生错误,则返回 EOF
类似于 getchar 和 putchar,getc 和 putc 是宏而不是函数
启动一个 C 语言程序时,操作系统环境负责打开 3 个文件,并将这 3 个文件的指针提供给该程序
这 3 个文件分别是标准输入、标准输出、标准错误
相应的文件指针分别为 stdin、stdout、stderr,它们在
在大多数环境中,stdin 指向键盘,而 stdout 和 stderr 指向显示器
我们从 7.1 节的讨论中可以知道,stdin 和 stdout 可以被重定向到文件或管道
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 程序了
如果有命令行参数,参数将被解释为文件名,并按顺序逐个处理,如果没有参数,则处理标准输入
#includemain(int argc, char *argv[]) { FILE *fp; void filecopy(FILE *, FILE *) if (argc == 1) filecopy(stdin, stdout); else while(--argc > 0) if ((fp = fopen(*++argv, "r")) == NULL) { printf("cat: can't open %sn, *argv); return 1; } else { filecopy(fp, stdout); fclose(fp); } return 0; } void filecopy(FILE *ifp, FILE *ofp) { int c; while ((c = getc(ifp)) != EOF) putc(c, ofp); }
文件指针 stdin 与 stdout 都是 FILE * 类型的对象,但它们是常量,而非变量,因此不能对它们赋值
函数 int fclose(FILE *fp) 执行和 fopen 相反的操作
它断开由 fopen 函数建立的文件指针和外部名之间的连接,并释放文件指针以供其它文件使用
因为大多数操作系统都限制了一个程序可以同时打开的文件数,所以,当文件指针不再需要时就应该释放,这是一个好的编程习惯
对输出文件执行 fclose 还有另外一个原因:它将把缓冲区中由 putc 函数正在收集的输出写到文件中
当程序正常终止时,程序会自动为每个打开的文件调用 fclose 函数
如果不需要使用 stdin 与 stdout,可以把它们关闭掉,也可以通过库函数 freopen 重新指定它们
参考文献:The C Programming Language
目录



