标准IO库
标准IO库处理有很多细节,例如缓冲区分配、以优化的块长度执行IO等。
1.流和FILE对象
对于标准IO库,所有操作都是围绕流
进行的,当用标准IO库打开或创建一个文件时,就使一个流与一个文件相关联。
流的定向决定读写字符是单字节还是多字节字符集(多字节对应宽定向,单字节对应字节定向)。fwide用于设置流的定向:
#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
/* 返回值:宽定向为正值;字节定向为负值;流是未定向,返回0 */
由于fwide无出错返回,因此在调用fwide前先清除errno,从fwrite返回时,检查errno的值。
2.缓冲
为了不频繁使用write和read,标准IO库提供了缓冲:
- 全缓冲,填满标准IO缓冲区后进行实际IO操作
- 行缓冲,遇到换行符或者填满行缓冲区后进行操作
- 不缓冲,不缓冲,直接输出
通过下列两个函数可以对缓冲类型进行修改:
#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf );
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
/* 返回值:成功返回0,失败返回非0 */
使用mode
参数说明所需的缓冲类型:
mode参数 | 类型 |
---|---|
_IOFBF | 全缓冲 |
_IOLBF | 行缓冲 |
_IONBF | 不缓冲 |
若不使用缓冲,则忽略buf
和size
参数。
3.打开流
下列3个函数用于打开流:
#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);
/* 返回值:成功返回文件指针,失败返回NULL */
三者区别在于:
-
fopen 打开路径名为
pathname
的一个指定文件 - freopen 在一个指定的流上打开一个指定的文件,若该流已经打开,则先关闭该流。若该流已经定向,则使用freopen清除该定向。此函数一般用于将指定文件打开为一个预定义的流:标准输入,输出和错误
- fdopen取一个已有的文件描述符(可能从open,dup,pipe,socket得到此描述符),并使一个标准IO流与该描述符向结合。此函数常用于创建管道和网络通信管道函数返回的描述符。
使用fclose可以关闭一个打开的流,在文件被关闭前,冲洗缓冲中的输出数据,并丢弃缓冲区中任何输入数据,并且释放此缓冲区。
一个进程正常终止时,则所有带未写缓冲数据的标志IO都会被冲洗,所有打开的标准IO流都被关闭。
4.读和写流
打开流之后,可以对其进行读写操作。
输入函数
以下3个函数用于一次读一个字符:
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
/* 返回值:成功返回下一个字符;若到达尾端或者出错返回EOF */
getchar等同于getc(stdin)。前两个函数区别在于,getc可被实现为宏,而fgetc不能,这就意味着:
- getc的参数不应当具有副作用的表达式
- fgetc一定是个函数,可以通过函数指针传递给另一个函数
- 调用fgetc所需时间可能比getc更长
输出函数
对应输入函数都有一个输出函数:
#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
/* 返回值:成功返回c,出错返回EOF*/
与输入函数一样,putchar(c)等同于putc(c,stdout),putc可以被实现为宏。
5.每次一行IO
下列两个函数提供每次输入一行的功能:
#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
/* 返回值:成返回buf;若到达尾端或者出错返回NULL*/
尽量使用fgets防止缓冲区溢出。
对应输出一行的功能:
#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
/* 返回值:成功返回非负值,出错返回EOF*/
fputs将一个null
字节终止的字符串写到指定的流,puts除了null
,随后添加了一个换行符到标准输出。
如果总是使用fgets和fputs,那么久会熟知在每行终止处我们必须字节处理换行符。
6.二进制IO
前面的输入函数只能一次性读取一个字节或者一行数据,为了获得整个结构,提供了下列两个函数用以执行二进制IO操作:
#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);
读写一个结构体,可以如下:
struct {
short count;
long total;
char name[NAMESIZE];
} item;
if (fwrite(&item, sizeof(item), 1, fp) != 1)
err_sys("fwrite error");