对于标准I/O库,它们的操作则是围绕流(stream)进行的。
流的定向(stream’s orientation)决定了所读、写的字符是单字节还是多字节的。
freopen函数清除一个流的定向;fwide函数设置流的定向。
#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
/* 返回值:若流是宽定向的则返回正值,若流是字节定向的则返回负值,若流是未定向的则返回0 */
mode为负,字节定向。
mode为正,宽定向。
mode为0,不设置,但返回该流定向的值。
注意,fwide并不改变已定向流的定向。fwide无出错返回。
三个标准I/O流:stdin、stdout、stderr。
5.1 缓冲
标准I/O库提供缓冲的目的是尽可能减少使用read和write调用的次数。
标准I/O库提供了三种类型的缓冲:
(1)全缓冲。在这种情况下,在填满标准I/O缓冲区后才进行实际I/O操作。
术语冲洗(flush)说明标准I/O缓冲区的写操作。在标准I/O库方面,flush(冲洗)意味着将缓冲区中的内容写到磁盘上。在终端驱动程序方面,flush(刷清)表示丢弃已存储在缓冲区中的数据。
(2)行缓冲。在这种情况下,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。
行缓冲有两个限制:一,标准I/O库用来收集每一行的缓冲区的长度是固定的,所以只要填满了缓冲区,即使没有写一个换行符,也进行I/O操作。二,任何时候只要通过标准I/O库要求从(a)一个不带缓冲的流,或者(b)一个行缓冲的流(它要求从内核得到数据)得到输入数据,那么就会造成冲洗所有行缓冲输出流。在(b)中带了一个括号中的说明,其理由是,所需的数据可能已在该缓冲区中,它并不要求在需要数据时才从内核读数据。很明显,从不带缓冲的一个流中进行输入((a)项)要求当时从内核得到数据。
常用:标准出错是不带缓冲的;打开至终端设备的流是行缓冲的;其他所有流是全缓冲的。
(3)不带缓冲。标准I/O库不对字符进行缓冲存储。
更改缓冲类型:
#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参数:
_IOFBF 全缓冲
_IOLBF 行缓冲
_IONBF 不带缓冲
如果在一个函数内分配一个自动变量类的标准I/O缓冲区,则从该函数返回之前,必须关闭该流。
#include <stdio.h>
int fflush(FILE *fp);
/* 返回值:若成功则返回0,若出错则返回EOF */
此函数使该流所有未写的数据都被传送至内核。若fp是NULL,则导致所有输出流被冲洗。
5.2 打开流
#include <stdio.h>
FILE *fopen(const char *restrict path, const char *restrict type);
FILE *freopen(const char *restrict path, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int fd, const char *type);
/* 返回值:若成功则返回文件指针,若出错则返回NULL */
(1)fopen打开一个指定的文件。
(2)freopen在一个指定的流上打开一个指定的文件,若流已经打开,则先关闭该流。若该流已经定向,则freopen清除该定向。
(3)fdopen获取一个现有的文件描述符,并使一个标准I/O流与该描述符相结合。
调用fclose关闭一个打开的流:
#include <stdio.h>
int fclose(FILE *fp);
/* 返回值:若成功则返回0,若出错则返回EOF */
文件被关闭之前,冲洗缓冲区中的输出数据。丢弃缓冲区中的任何输入数据。如果标准I/O库已经为该流自动分配了一个缓冲区,则释放此缓冲区。
当一个进程正常终止时(直接调用exit函数,或者从main函数返回),则所有未写缓冲数据的标准I/O流都会被冲洗,所有打开的标准I/O流都会被关闭。
5.3 读、写流
(1)每次一个字符的I/O。
(2)每次一行的I/O。fgets和fputs。
(3)直接I/O。fread和fwrite。
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
/* 返回值:若成功则返回下一个字符,若已达到文件结尾或者出错则返回EOF */
int ferror(FILE *fp);
int feof(FILE *fp);
/* 返回值:若条件为真则返回非0值,否则返回0 */
void clearerr(FILE *fp);
int ungetc(int c, FILE *fp);
/* 返回值:若成功则返回c,若出错则返回EOF */
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
/* 返回值:若成功则返回c,若出错则返回EOF */
在多数实现中,为每个流在FILE对象中维持了两个标志:
- 出错标志
- 文件结束标志
调用clearerr则清除这两个标志。
从流中读取数据以后,可以调用ungetc将字符再压送回流中。
#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
/* 返回值:若成功则返回buf,若已到达文件结尾或出错则返回BULL */
char *fputs(const char *restrict str, FILE *restrict fp);
char *puts(const char *str);
/* 返回值:若成功则返回非负值,若出错则返回EOF */
请使用fgets。原因:一、gets不能指定缓冲区的长度,可能造成缓冲区溢出。二、gets并不将换行符存入缓冲区中。
#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);
/* 返回值:读或写的对象数 */
5.4 定位流
#include <stdio.h>
long ftell(FILE *fp);
/* 返回值:若成功则返回当前文件位置指示,若出错则返回-1L */
int fseek(FILE *fp, long offset, int whence);
/* SEEK_SET、SEEK_CUR、SEEK_END
返回值:若成功则返回0,若出错则返回非0值
*/
void rewind(FILE *fp);
off_t ftello(FILE *fp);
/* 返回值:若成功则返回当前文件位置指示,若出错则返回-1 */
int fseeko(FILE *fp, off_t offset, int whence);
/* 返回值:若成功则返回0,若出错则返回非0值 */
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);
/* 返回值:若成功则返回0,若出错则返回非0值 */
5.5 格式化I/O
#include <stdio.h>
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
/* 返回值:若成功则返回输出字符数,若出错则返回负值 */
int sprintf(char *restrict buf, const char *restrict format, ...);
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
/* 返回值:若成功则返回存入数组的字符数,若出错则返回负值 */
转换说明:
%[flags][fldwidth][precision][lenmodifier]convtype
flags:
fldwidth说明转换的最小字段宽度。
precision说明整型转换后最少输出数字位数、浮点数转换后小数点后的最少位数。
lenmodifier说明参数长度:
convtype:
#include <stdio.h>
#include <stdarg.h>
int vprintf(const char *restrict format, va_list arg);
int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg);
/* 返回值:若成功则返回输出字符数,若出错则返回负值 */
int vsprintf(char *restrict buf, const char *restrict format, va_list arg);
int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg);
/* 返回值:若成功则返回存入数组的字符数,若出错则返回负值 */
#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, ...);
/* 返回值:指定的输入项数;若出错或在任意变换前以到达文件结尾则返回EOF */
转换说明:
%[*][fldwidth][lenmodifier]convtype
可选的(*)用于抑制转换。
fldwidth说明最大宽度。
lenmodifier说明要用转换结果初始化的参数大小。
convtype:
#include <stdarg.h>
#include <stdio.h>
int vscanf(const char *restrict format, va_list arg);
int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg);
int vsscanf(const char *restrict buf, const char *restrict format, va_list arg);
/* 返回值:指定的输入项数;若出错或在任意变换前以到达文件结尾则返回EOF */
5.6 其他
每一个标准I/O流都有一个与其相关联的文件描述符。用fileno函数获取其描述符。
#include <stdio.h>
int fileno(FILE *fp);
/* 返回值:与该流相关联的文件描述符 */
创建临时文件:
#include <stdio.h>
char *tmpnam(char *ptr);
/* 返回值:指向唯一路径名的指针 */
FILE *tmpfile(void);
/* 返回值:若成功则返回文件指针,若出错则返回NULL */
char *tempnam(const char *directory, const char *prefix);
/* 返回值:指向唯一路径名的指针 */
#include <stdlib.h>
int mkstemp(char *template);
/* 返回值:若成功则返回文件描述符,若出错则返回-1 */
- mkstemp创建的临时文件不会自动删除。我们需要自行unlink它。
- tmpnam和tempnam的不足之处:在返回唯一路径名和应用程序用该路径名创建文件之间有一个时间窗口。在该时间窗口期间,另一个进程可能创建一个同名文件。tmpfile和mkstemp则不会。
最后,标准I/O的替代库:
sfio、mmap函数、uClibc C库、newlibc C库