标准库I/O库处理很多细节,如缓冲区分配,以优化的块长度执行I/O等
当打开一个文件时,即返回一个文件描述符,然后该文件描述符就用于后续的I/O操作。而对于标准I/O库,它们的操作时围绕流(stream)进行的。
流可用于单字节或多字节(“宽”)字符集
#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
//返回值:若流是宽定向的,返回正值;若流是字节定向的,返回负值;若流是为定向的,返回0
根据mode参数的不同值,fwide函数执行不同的工作。
1. 如若mode参数值为负,fwide将试图使指定的流是字节定向的。
2. 如若mode参数值为正,fwide将试图使指定的流是宽定向的。
3. 如若mode参数值为0,fwide将是不试图设置流的定向,但返回标志该流定向的值。
ISO C要求下列缓冲特征:
很多系统默认使用下列缓冲
#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
#include <stdio.h>
int fflush(FILE *fp);
//返回值:若成功,返回0;若出错,返回EOF
此函数使该流所有未写的数据都被传送至内核。作为一种特殊情形,如若fp是NULL,则此函数将导致所有输出流被冲洗。
#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
区别:
fdopen函数取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合。
type | 说明 | open(2)标志 |
---|---|---|
r/rb | 读打开 | O_RDONLY |
w/wb | 写打开 | O_WRONLY or O_CREAT or O_TRUNC |
a/ab | 追加 | O_WRONLY or O_CREAT or O_APPEND |
r+/r+b/rb+ | 读写打开 | O_RDWR |
w+/w+b/wb+ | 读写打开 | O_RDWR or O_CREAT or O_TRUNC |
a+/a+b/ab+ | 文件尾读写打开 | O_RDWR or O_CREAT or O_APPEND |
当以读和写类型打开一个文件时。具有下列限制
#include <stdio.h>
int fclose(FILE *fp);
//返回值:若成功,返回0;若出错,返回EOF
在该文件被关闭之前,冲洗缓冲中发热输出数据。缓冲区中的任何输入数据被丢弃。
三种不同类型的非格式化I/O中进行选择
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
//3个函数的返回值:若成功,返回下一个字符;若已达到文件尾端或出错,返回EOF
函数getchar等同于getc(stdin)。前两个函数的区别是,getc可被实现为宏,而fgetc不能实现为宏。这意味着以下几点。
区分EOF和错误发生:
#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
//两个函数返回值:若条件为真,返回非0(真);否则,返回0(假)
void clearerr(FILE *fp);
在大多数实现中,为每个流在FILE对象中维护了两个标志:
调用clearerr可以清除这两标志。 从流中读取数据以后,可以调用ungetc将字符再压送回流中。回送的字符,不一定必须是上一次读到的字符。不能回送EOF。一次成功的ungetc调用会清除该流的文件结束标志。
#include <stdio.h>
int ungetc(itn c, FILE *fp);
//返回值:若成功,返回c;若出错,返回EOF
下面两个函数提供每次输入一行的功能
#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
//3个函数返回值:若成功,返回c;若出错返回EOF
与输入函数一样,putchar(c)等同于putc(c, stdout),putc可被实现为宏,而fputc不能实现为宏。
char *fgets(char * restrict str, int size, FILE * restrict stream);
char *gets(char *str);
这两个函数都指定了缓冲区的地址,读入的行将送入其中。gets从标准输入读,而fgets则从指定的流读。对于fget,必须指定缓冲的长度n,此函数一直读到下一个字符为换行符为止,但是不超过n。
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随后又将一个换行符写出到标注输出。(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(void *restrict ptr, size_t size, size_t nobj, FILE
*restrict fp);
//两个函数的返回值:读或写的对象数
指定size为每个数组元素的长度,nobj为元素个数.
有3中方法定位标准I/O流
#include <stdio.h>
long ftell(FILE *fp);
//返回值:若成功,返回当前文件位置指示;若出错,返回-1L
int fseek(FILE *fp, long offset, int whence);
//返回值:若成功,返回0;若出错,返回-1
void rewind(FILE *fp);
#include <stdio.h>
off_t ftello(FILE *fp);
//返回值:若成功,返回当前文件位置;若出错,返回(off_t)-1
int fseeko(FILE *fp, off_t offset, int whence);
//返回值:若成功,返回0;若出错,返回-1
#include <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);
//两个函数返回值:若成功,返回0;若出错,返回非0
#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,...);
//三个函数返回值:若成功,返回输出的字符数;若输出出错,返回负值
int sprintf(char *restrict buf, const char *restrict format,...);
//返回值:若成功,返回存入数组的字符数;若编码出错,返回负值
int snprintf(char *restrict buf, size_t n, const char *restrict
format,...);
//返回值:若缓冲区足够大,返回将要存入数组的字符数;若编码出错,返回负值
一个转换说明有4个可选部分:
% [flags] [fldwidth] [precision] [lenmodifier] convtype
标志 | 说明 |
---|---|
‘ | 将整数按千分位组字符 |
- | 在字段内左对齐输出 |
+ | 总是显示带符号转换的正负号 |
(空格) | 如果第一个字符不是正负号,则在其前面加上一个空格 |
0 |添加前导0进行填充
转换类型 | 说明 |
---|---|
d、i | 有符号十进制 |
o | 无符号八进制 |
u | 无符号十进制 |
x,X | 无符号十六进制 |
f, F | 双精度浮点数 |
e, E | 指数格式双精度浮点数 |
g, G | 根据转换后的值解释为f、F、e或E |
a, A | 十六进制指数格式双进度浮点数 |
c | 字符 |
s | 字符串 |
p | 指向void的指针 |
n | 到目前为止,此printf调用输出的字符的数目将被写入到指针所指向的带符号整型中 |
% | 一个%字符 |
C | 宽字符 |
S | 宽字符串 |
#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
标准I/O库最终都要调用不带缓冲的I/O,每一个标准I/O流都有一个与其关联的文件描述符,可以对一个流调用fileno函数以获得文件描述符。
#include <stdio.h>
int fileno(FILE *fp);
//返回值:与该流相关联的文件描述符
创建临时文件的两个函数
#include <stdio.h>
char *tmpnam(char *ptr);
//返回值:指向唯一路径名的指针
FILE *tmpfile(void);
//返回值:若成功,返回文件指针;若出错,返回NULL
XSI的扩展:
#include <stdlib.h>
char *mkdtemp(char *template);
//返回值:若成功,返回指向目录名的指针;若出错,返回NULL
int mkstemp(char *template);
//返回值:若成功,返回文件描述符;若出错,返回-1
我们可以直接调用setbuf和setvbuf函数让I/O库使用我们自己的缓冲区.
创建内存流的三个函数:
#include <stdio.h>
FILE *fmenopen(void *restrict buf, size_t size, const char
*restrict type);
//返回值:若成功,返回流指针;若错误,返回NULL
fmenopen运行调用者提供缓存区用于内存流
type | 说明 |
---|---|
r过rb | 为读而打开 |
w或wb | 为写而打开 |
a或ab | 追加;为再第一个null字节处写而打开 |
r+或r+b或rb+ | 为读和写打开 |
w+或w+b或wb+ | 把文件截断至0长,为读和写打开 |
a+或a+b或ab+ | 追加;为再第一个null字节处读和写而打开 |
#include <stdio.h>
FILE *open_memstream(char **bufp, size_t *sizep);
#include <wchar.h>
FILE *open_wmenstream(wchar_t **bufp, size_t *sizep);
//两个函数的返回值:若成功,返回流指针;若出错,返回NULL
open_menstream函数创建的流是面向字节的,open_wmemstream函数创建的流是面向宽字节的。这两个函数与fmenopen函数的不同在于: