《UNIX 环境高级编程》学习笔记—— 标准I/O库

流和 FILE 对象

对于标准I/O库,它们的操作是围绕进行的。

当用标准IO库打开或创建一个文件时,我们已使一个流与一个文件相关联。

标准I/O文件流可用于单字节或多字节(“宽”)字符集。

流的定向决定了所读、写的字符是单字节还是多字节。当一个流最初被创建时,它并没有定向。

如若在未定向的流上使用一个多字节I/O函数(见<wchar.h>),则将该流的定向设置为宽定向的。
若在未定向的流上使用一个单字节I/O函数,则将该流的定向设为字节定向的。

只有两个函数可改变流的定向。
freopen 函数清除一个流的定向;
fwide 函数可用于设置流的定向。

#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fd, int mode);

                     返回值:若流是宽定向的,返回正值:若流是字节定向的,返回负值:若流是未定向的,返回0

当打开一个流时,标准I/O函数 fopen 返回一个指向 FILE 对象的指针。
该对象通常是一个结构,它包含了标准I/O库为管理该流需要的所有信息,包括用于实际 I/O 的文件描述符、指向用于该流缓冲区的指针、缓冲区的长度、当前在缓冲区中的字符数以及出错标志等。

为了引用一个流,需将FILE指针作为参数传递给每个标准I/O函数。

标准输入、标准输出和标准错误

对一个进程预定义了3个流,并且这3个流可以自动地被进程使用,它们是:标准输入、标准输出和标准错误。

这3个标准I/O流通过预定义文件指针 stdin、stdout 和 stderr 加以引用。
这3个文件指针定义在头文件 <stdio.h> 中。

缓冲

标准I/O提供了以下3种类型的缓冲。

  • (1)全缓冲。
    在这种情况下,在填满标准I/O缓冲区后才进行实际I/O操作。
    冲洗说明标准I/O缓冲区的写操作。

  • (2)行缓冲。
    在这种情况下,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。
    当流涉及一个终端时(如标准输入和标准输出),通常使用行缓冲。
    对于行缓冲由两个限制。

    • 第一,因为标准I/O库用来收集每一行的缓冲区的长度是固定的,所以只要填满了缓冲区,那么即使还没有写入一个换行符,也进行I/O操作。
    • 第二,任何时候只要通过标准I/O库要求从 (a)一个不带缓冲的流,或者 (b)一个行缓冲的流(它从内核请求需要数据)得到输入数据,那么就会冲洗所有行缓冲输出流。
  • (3)不带缓冲。
    标准I/O库不对字符进行缓冲存储。

在这里插入图片描述

打开流

下列3个函数打开一个标准I/O流。

#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int fd, const char *type);

                             3个函数的返回值:若成功,返回文件指针;若出错,返回 NULL

这3个函数的区别如下。

  • (1)fopen 函数打开路径名为 pathname 的一个指定的文件。
  • (2)freopen 函数在一个指定的流上打开一个指定的文件,如若该流已经打开,则先关闭该流。若该流已经定向,则使用 freopen 清除该定向。
    此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标志错误。
  • (3)fdopen 函数取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合。
    此函数常用于由创建管道和网络通信通道函数返回的描述符。

在这里插入图片描述
调用 fclose 关闭一个打开的流。

#include <stdio.h>
int fclose(FILE *fp);

                         返回值:若成功,返回0;若出错,返回 EOF

读和写流

一旦打开了流,则可在3种不同类型的非格式化I/O中进行选择,对其进行读、写操作。

  • (1)每次一个字符的I/O。 一个读或写一个字符,如果流是带缓冲的,则标准I/O函数处理所有缓冲。
  • (2)每次一行的I/O。 如果想要一次读或写一行,则使用 fgets 和 fputs 。每行都以一个换行符终止。当调用 fgets 时,应说明能处理的最大行长。
  • (3)直接I/O。 fread 和 fwrite 函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中每次读或写一个结构。

1. 输入函数
以下3个函数可用于一次读一个字符。

#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
                         3个函数的返回值:若成功,返回下一个字符;若已达到文件尾端或出错,返回 EOF

2. 输出函数
对应于上面所述的每个输入函数都有一个输出函数。

#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
                           3个函数返回值:若成功,则返回c;若出错,返回 EOF

每次一行 I/O

下面两个函数提供每次输入一行的功能。

#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
                        两个函数返回值:若成功,返回buf;若已到达文件尾端或出错,返回 NULL

这两个函数都指定了缓冲区的地址,读入的行将送入其中。
gets 从标准输入读,而 fgets 则从指定的流读。

对于 fgets,必须指定缓冲的长度 n 。
gets 是一个不推荐使用的函数。其问题是调用者在使用 gets 时不能指定缓冲区的长度。

fputs 和 puts 提供每次输出一行的功能。

#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
                              两个函数返回值:若成功,返回非负值;若出错,返回 EOF

函数 fputs 将一个以 null 字节终止的字符串写到指定的流,尾端的终止符 null 不写出。
puts 将一个以 null 字节终止的字符串写到标准输出,终止符不写出。但是,puts 随后又将一个换行符写到标准输出。

二进制 I/O

提供了下列两个函数以执行二进制I/O操作。

#include <stdio.h>
size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
                                             两个函数的返回值:读或写的对象数

这些函数有以下两种常见的用法。

  • (1)读或写一个二进制数组。
  • (2)读或写一个结构。

使用二进制I/O的基本问题是,它只能用于读在同一系统上已写的数据。
常常有这种情形,在一个系统上写的数据,要在另一个系统上进行处理。
在这种环境下,这两个函数可能就不能正常工作,其原因是:

  • (1)在一个结构中,同一成员的偏移量可能随编译程序和系统的不同而不同(由于不同的对齐要求)。
  • (2)用来存储多字节整数和浮点值的二进制格式在不同的系统结构间也可能不同。

定位流

有3种方法定位标准I/O流。

  • (1)ftell 和 fseek 函数。
    这两个函数自 V7 以来就存在了,但是它们都假定文件的位置可以存放在一个长整型中。
  • (2)ftello 和 fseeko 函数。
    Single UNIX Specification 引入了这两个函数,使文件偏移量可以不必一定使用长整型。
    它们使用 off_t 数据类型代替了长整型。
  • (3)fgetpos 和 fsetpos 函数。
    这两个函数是由 ISO C 引入的。
    它们使用一个抽象数据类型 fpos_t 记录文件的位置。
    这种数据类型可以根据需要定义为一个足够大的数,用以记录文件位置。

格式化 I/O

1. 格式化输出
格式化输出是由5个 printf 函数来处理的。

#include <stdio.h>
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);
                           3个函数返回值:若成功,返回输出字符数;若输出出错,返回负值

int sprintf(char *restrict buf, const char *restrict format, ...);
                             返回值:若成功,返回存入数组的字符数;若编码出错,返回负值

int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
                             返回值:若缓冲区足够大,返回将要存入数组的字符数;若编码出错,返回负值

printf 将格式化数据写到标准输出。
fprintf 写至指定的流。
dprintf 写至指定的文件描述符。
sprintf 将格式化的字符送入数组 buf 中。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2. 格式化输入

执行格式化输入处理的是3个 scanf 函数。

#include <stdio.h>
int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ....);
                            3个函数返回值:赋值的输入项数;若输入出错或在任一转换前已到达文件尾端,返回 EOF

在这里插入图片描述

临时文件

ISO C 标准I/O库提供了两个函数以帮助创建临时文件。

#include <stdio.h>
char *tmpnam(char *ptr);  
                           返回值:指向唯一路径名的指针
FILE *tmpfile(void);
                           返回值:若成功,返回文件指针;若出错,返回 NULL

tmpnam 函数产生一个与现有文件名不同的一个有效路径名字符串。每次调用它时,都产生一个不同的路径名,最多调用次数是 TMP_MAX。

tmpfile 创建一个临时二进制文件(类型 wb+),在关闭该文件或程序结束时将自动删除这种文件。

内存流

有3个函数可用于内存流的创建,第一个是 fmemopen 函数。

#include <stdio.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);
                            返回值:若成功,返回流指针;若出错,返回 NULL

fmemopen 函数允许调用者提供缓冲区用于内存流:buf 参数指向缓冲区的开始位置,size 参数指定了缓冲区大小的字节数。

type 参数控制如何使用流。

在这里插入图片描述

用于创建内存流的其他两个函数分别是 open_memstream 和 open_wmemstream 。

#include <stdio.h>
FILE *open_memstream(char **bufp, size_t *sizep);

#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);
                                   两个函数的返回值:若成功,返回流指针;若出错,返回 NULL

open_memstream 函数创建的流是面向字节的。
open_wmemstream 函数创建的流是面向宽字节的。

这两个函数与 fmemopen 函数的不同在于:

  • 创建的流只能写打开;
  • 不能指定自己的缓冲区,但可以分别通过 bufp 和 sizep 参数访问缓冲区地址和大小;
  • 关闭流后需要自行释放缓冲区;
  • 对流添加字节会增加缓冲区大小。

学习参考资料:

《UNIX 环境高级编程》第3版
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值