1 缓冲
标准I/O库提供缓冲的目的是尽可能地减少使用read和write调用的次数。他也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。不幸的是,标准I/O库最令人迷惑的也是他的缓冲。 标准I/O提供了三种类型的缓冲: 1、全缓冲。这种情况下,在填满标准I/O缓冲区后才进行实际I/O操作。对于驻留在磁盘上的文件通常是由标准I/O库实施全缓冲。一个流上执行第一次I/O操作时,相关标准I/O函数通常调用malloc获得需使用的缓冲区。 术语冲洗说明I/O缓冲区的写操作。缓冲区可由标准I/O例程自动冲洗,或者可以调用函数fflush冲洗一个流。值得引起注意的是在UNIX环境 中,flush有两种意思。在标准I/O库方面,flush意味着将缓冲区中的内容写到磁盘上。在终端驱动程序方面flush表示丢弃已存储在缓冲区中的数据。 2、行缓冲。在这种情况下,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。这允许我们一次输出一个字符,但只有在写了一行之后才进行实际I/O操作。当流涉及一个终端时,通常使用行缓冲。 3、不带缓冲。标准I/O库不对字符进行缓冲存储。例如,如果用I/O函数fputs写15个字符到不带缓冲的流中,则该函数很可能用write系统调用函数将这些字符立即写至相关联的打开文件中。 ISO C要求下列缓冲特征:
- 当且仅当标准输入和标准输出并不涉及交互式设备使,他们才是全缓冲的。
- 标准出错绝不会是全缓冲的。
但是,这并没有告诉我们如果标准输入和标准输出涉及交互式设备时,他们是不带缓冲的还是行缓冲的;以及标准出错时不带缓冲的还是行缓冲的。很多系统默认使用下列类型的缓冲:
- 标准出错是不带缓缓冲的。
- 如若是涉及终端设备的其他流,则他们是行缓冲的;否则是全缓冲的。
可调用下列函数中的一个更改缓冲类型
代码语言:javascript复制#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
Returns: 0 if OK, nonzero on error
2 打开流
三个函数可以打开一个标准IO流
代码语言:javascript复制#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 filedes, const char *type);
All three return: file pointer if OK, NULL on error
三者的区别在于:
- fopen函数:打开路径名为pathname的一个指定的文件
- fdopen函数:打开已存在的文件描述符,使标准I/O流与该文件相结合。主要用于fopen不能打开的特殊文件(如管道和网路通信等)。这时必须先调用设备专用函数以获得一个文件描述符,然后在用fdopen使一个标准I/O与该文件描述符相结合。
- freopen函数:在指定的流上打开一个指定的文件,如若该流已经打开,则先关闭该流。若该流已经定向,则使用freopen清除该定向。简单的说可以利用freopen函数重定向。此函数一般用于将一个指定的文件代开为一个预定义的流:stdout,stdin,stderr。
3 读和写流
一旦打开了流,则有三种不同类型的非格式化IO进行选择,对其进行读写操作 (1)每次一个字符的IO。一次读或者写一个字符,如果流是带缓冲的,则标准IO会处理所有缓冲 (2)每次一行IO。就要使用fgets和fputs,每行都以一个换行符终止。 (3)直接IO。fread和fwrite支持这种类型的IO。
4 一次一个字符的IO
代码语言:javascript复制#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
All three return: next character if OK, EOF on end of file or error
对应的输出函数
代码语言:javascript复制#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
All three return: c if OK, EOF on error
5 每次一行IO
代码语言:javascript复制#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
Both return: buf if OK, NULL on end of file or error
fgets必须指定缓冲区长度n。此函数一直读到下一个换行符为止,但是不超过n-1个字符,缓冲区以null字符结尾。gets不推荐,因为没有指定缓冲区大小,可能导致溢出。
代码语言:javascript复制#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
Both return: non-negative value if OK, EOF on error
fputs是将一个以null字符结尾的字符串写到指定的流,null不写出。通常null之前是换行符,但并不总是如此。
6 二进制流
通常用来读取数据结构struct,C语言书中提到过的。
代码语言:javascript复制#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);
Both return: number of objects read or written
(1)ptr:是一个指针,对fwrite来说,是要获取数据的地址; (2)size:要写入内容的单字节数; (3)nobj:要进行写入size字节的数据项的个数; (4)fp:目标文件指针; 这些函数有如下两种常见用法(二进制数据或者结构体)
代码语言:javascript复制float data[10];
if (fwrite(&data[2], sizeof(float), 4, fp) != 4)
err_sys("fwrite error");
代码语言:javascript复制struct {
short count;
long total;
char name[NAMESIZE];
} item;
if (fwrite(&item, sizeof(item), 1, fp) != 1)
err_sys("fwrite error");
但是由于系统环境不同可能无法工作 (1)在一个结构中,同一个成员的偏移量可能因编译器和系统而异 (2)用来存储字节整数和浮点值的二进制格式在不同的机器体系结构间也可能不同
7 格式化IO
代码语言:javascript复制#include <stdio.h>
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
Both return: number of characters output if OK, negative value if output error
int sprintf(char *restrict buf, const char *restrict format, ...);
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
Both return: number of characters stored in array if OK, negative value if encoding error