标准输入输出<stdio.h>
头文件<stdio.h>定义了用于输入和输出的函数、类型和宏。最重要的类型是用于声明文件指针的FILE。另外两个常用的类型是size_t和fpos_t,size_t是由运算符sizeof产生的无符号整类型;fpos_t类型定义能够唯一说明文件中的每个位置的对象。由头部定义的最有用的宏是EOF,其值代表文件的结尾。
1、文件操作
(1) fopen
FILE *fopen(const char *filename, const char *mode);
返回:成功为FILE指针,失败为NULL
打开以filename所指内容为名字的文件,返回与之关联的流。
mode决定打开的方式,可选值如下:
"r" 打开文本文件用于读
"w" 创建文本文件用于写,并删除已存在的内容(如果有的话)
"a" 添加;打开或创建文本文件用于在文件末尾写
"rb" 打开二进制文件用于读
"wb" 创建二进制文件用于写,并删除已存在的内容(如果有的话)
"ab" 添加;打开或创建二进制文件用于在文件末尾写
"r+" 打开文本文件用于更新(即读和写)
"w+" 创建文本文件用于更新,并删除已存在的内容(如果有的话)
"a+" 添加;打开或创建文本文件用于更新和在文件末尾写
"rb+"或"r+b" 打开二进制文件用于更新(即读和写)
"wb+"或"w+b" 创建二进制文件用于更新,并删除已存在的内容(如果有的话)
"ab+"或"a+b" 添加;打开或创建二进制文件用于更新和在文件末尾写
后六种方式允许对同一文件进行读和写,要注意的是,在写操作和读操作的交替过程中,必须调用fflush()或文件定位函数如fseek()、fsetpos()、rewind()等。
文件名filename的长度最大为FILENAME_MAX个字符,一次最多可打开FOPEN_MAX个文件(在<stdio.h>中定义)。
(2) freopen
#include <stdio.h>
FILE *freopen(const char *filename, const char *mode, FILE *stream);
返回:成功为stream,失败为NULL
以mode指定的方式打开文件filename,并使该文件与流stream相关联。freopen()先尝试关闭与stream关联的文件,不管成功与否,都继续打开新文件。
该函数的主要用途是把系统定义的标准流stdin、stdout、stderr重定向到其他文件。
(3)fflush
#include <stdio.h>
int fflush(FILE *stream);
返回:成功为0,失败返回EOF
对输出流(写打开),fflush()用于将已写到缓冲区但尚未写出的全部数据都写到文件中;对输入流,其结果未定义。如果写过程中发生错误则返回EOF,正常则返回0。
fflush(NULL)用于刷新所有的输出流。
程序正常结束或缓冲区满时,缓冲区自动清仓。
(4) fclose
#include <stdio.h>
int flcose(FILE *stream);
返回:成功为0,失败返回EOF
刷新stream的全部未写出数据,丢弃任何未读的缓冲区内的输入数据并释放自动分配的缓冲区,最后关闭流。
(5) remove
#include <stdio.h>
int remove(const char *filename);
返回:成功为0,失败为非0值
删除文件filename。
(6)rename
#include <stdio.h>
int rename(const char *oldfname, const char *newfname);
返回:成功为0,失败为非0值
把文件的名字从oldfname改为newfname。
(7)tmpfile
#include <stdio.h>
FILE *tmpfile(void);
返回:成功为流指针,失败为NULL
以方式"wb+"创建一个临时文件,并返回该流的指针,该文件在被关闭或程序正常结束时被自动删除
(8) tmpnam
#include <stdio.h>
char *tmpnam(char s[L_tmpnam]);
返回:成功为非空指针,失败为NULL
若参数s为NULL(即调用tmpnam(NULL)),函数创建一个不同于现存文件名字的字符串,并返回一个指向一内部静态数组的指针。
若s非空,则函数将所创建的字符串存储在数组s中,并将它作为函数值返回。s中至少要有L_tmpnam个字符的空间。
tmpnam函数在每次被调用时均生成不同的名字。在程序的执行过程中,最多只能确保生成TMP_MAX个不同的名字。注意tmpnam函数只是用于创建一个名字,而不是创建一个文件。
(9) setvbuf
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
返回:成功返回0,失败返回非0
控制流stream的缓冲区,这要在读、写以及其他任何操作之前设置。
如果buf非空,则将buf指向的区域作为流的缓冲区,如果buf为NULL,函数将自行分配一个缓冲区。
size决定缓冲区的大小。
mode指定缓冲的处理方式,有如下值:
_IOFBF,进行完全缓冲;
_IOLBF,对文本文件表示行缓冲;
_IOLNF,不设置缓冲
(10)setbuf
#include <stdio.h>
void setbuf(FILE *stream, char *buf);
如果buf为NULL,则关闭流stream的的缓冲区;否则setbuf函数等价于:
(void)setvbuf(stream, buf, _IOFBF, BUFSIZ)。
注意自定义缓冲区的尺寸必须为BUFSIZ个字节。
2、格式化输出
(1) fprintf
#include <stdio.h>
int fprintf(FILE *stream, const char *format,…);
返回:成功为实际写出的字符数,出错返回负值
按照format说明的格式把变元表中变元内容进行转换,并写入stream指向的流。
格式化字符串由两种类型的对象组成:普通字符(它们被拷贝到输出流)与转换规格说明(它们决定变元的转换和输出格式)。每个转换规格说明均以字符%开头,以转换字符结束。如果%后面的字符不是转换字符,那么该行为是未定义的。
转换字符列表如下:
字 符 说明
d, i int;有符号十进制表示法
o unsigned int;无符号八进制表示法(无前导0)
x, X unsigned int;无符号十六进制表示法(无前导0X和0x),对0x用abcdef,对0X用ABCDEF
u unsigned int;无符号十进制表示法
c int;单个字符,转换为unsigned char类型后输出
s char *;输出字符串直到'\0'或者达到精度指定的字符数
f double;形如[-]mmm.ddd的十进制浮点数表示法,d的数目由精度确定。缺省精度为6位,精度为0时不输出小数点
e, E double;形如[-]m.dddddde[+-]xx或者[-]m.ddddddE[+-]xx的十进制浮点数表示法,d的数目由精度确定。缺省精度为6位,精度为0时不输出小数点
g G double;当指数值小于-4或大于等于精度时,采用%e或%E的格式;否则使用%f的格式。尾部的0与小数点不打印
p void *;输出指针值(具体表示与实现相关)
n int *;到目前为止以此格式调用函数输出的字符的数目将被写入到相应变元中,不进行变元转换
% 不进行变元转换,输出%
在%与转换字符之间依次可以有下列标记:
标记 说明
- 指定被转换的变元在其字段内左对齐
+ 指定在输出的数前面加上正负号
空格 如果第一个字符不是正负号,那么在其前面附加一个空格
0 对于数值转换,在输出长度小于字段宽度时,加前导0
# 指定其他输出格式,对于o格式,第一个数字必须为零;对于x/X格式,指定在输出的非0值前加0x或0X;对于e/E/f/g/G格式,指定输出总有一个小数点;对于g/GG格式,还指定输出值后面无意义的0将被保留。
宽度[number] 一个指定最小字段宽的数。转换后的变元输出宽度至少要达到这个数值。如果变元的字符数小于此数值,那么在变元左/右边添加填充字符。填充字符通常为空格(设置了0标记则为0)。
. 点号用于把字段宽和精度分开
精度[number] 对于字符串,说明输出字符的最大数目;对于e/E/f格式,说明输出的小数位数;对于g/G格式,说明输出的有效位数;对于整数,说明输出的最小位数(必要时可加前导0)
h/l/L 长度修饰符,h表示对应的变元按short或unsigned short类型输出;l表示对应的变元按long或unsigned long类型输出;L表示对应的变元按long double类型输出
在格式串中字段宽度和精度二者都可以用*来指定,此时该值可通过转换对应的变元来获得,这些变元必须是int类型。
(2) printf
int printf(const char *format, …);
printf(...)等价于fprintf(stdout, ...)。
(3) sprintf
#include <stdio.h>
int sprintf(char *buf, const char *format, …);
返回:实际写到字符数组的字符数,不包括'\0'
与printf()基本相同,但输出写到字符数组buf而不是stdout中,并以'\0'结束。
注意,sprintf()不对buf进行边界检查,buf必须足够大,以便能装下输出结果。
(4) snprintf
#include <stdio.h>
int snprintf(char *buf, size_t num, const char *format, …);
除了最多为num-1个字符被存放到buf指向的数组之外,snprintf()和sprintf()完全相同。数组以'\0'结束。
该函数不属于C89(C99增加的),但应用广泛,所以将其包括了进来。
1.2.5 vprintf
1.2.6 vfprintf
1.2.7 vsprintf
1.2.8 vsnprintf
#include <stdarg.h>
#include <stdio.h>
int vprintf(char *format, va_list arg);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vsprintf(char *buf, const char *format, va_list arg);
int vsnprintf(char *buf, size_t num, const char *format, va_list arg);
这几个函数与对应的printf()等价,但变元表由arg代替。
vsnprintf是C99增加的。
3、格式化输入
(1) fscanf
int fscanf(FILE *stream, const char *format, …);
返回:成功为实际被转换并赋值的输入项数目,到达文件尾或变元被转换前出错为EOF
在格式串format的控制下从流stream中读入字符,把转换后的值赋给后续各个变元,在此每一个变元都必须是一个指针。当格式串format结束时函数返回。
格式串format通常包含有用于指导输入转换的转换规格说明。格式串中可以包含:
空格或制表符,他们将被忽略;
普通字符(%除外),与输入流中下一个非空白字符相匹配;
转换规格说明:由一个%、一个赋值屏蔽字符*(可选)、一个用于指定最大字段宽度的数(可选)、一个用于指定目标字段的字符h/l/L(可选)、一个转换字符组成。
转换规格说明决定了输入字段的转换方式。通常把结果保存在由对应变元指向的变量中。然而,如果转换规格说明中包含有赋值屏蔽字符*,例如%*s,那么就跳过对应的输入字段,不进行赋值。输入字段是一个由非空白符组成的字符串,当遇到空白符或到达最大字段宽(如果有的话)时,对输入字段的读入结束。这意味着scanf函数可以跨越行的界限来读入其输入,因为换行符也是空白符(空白符包括空格、横向制表符、纵向制表符、换行符、回车符和换页符)。
转换字符列表如下:
字符 输入数据;变元类型
d 十进制整数;int *
i 整数;int *。该整数可以是以0开头的八进制数,也可以是以0x/0X开头的十六进制数
o 八进制数(可以带或不带前导0);unsigned int *
u 无符号十进制整数;unsigned int *
x 十六进制整数(可以带或不带前导0x/0X);unsigned int *
c 字符;char *。按照字段宽的大小把读入的字符保存在指定的数组中,不加入字符'\0'。字段宽的缺省值为1。在这种情况下,不跳过空白符;如果要读入下一个非空白符,使用%1s(数字1)
s 有非空白符组成的字符串(不包含引号);char *。该变元指针指向一个字符数组,该字符数组有足够空间来保存该字符串以及在末尾添加的'\0'
e/f/g 浮点数;float *。float浮点数的输入格式为:一个任选的正负号,一串可能包含小数点的数字和一个任选的指数字段。指数字段由字母e/E以及后跟的一个可能带正负号的整数组成
p 用printf("%p")调用输出的指针值;void *
n 将到目前为止此调用所读的字符数写入变元;int *。不读入输入字符。不增加转换项目计数
[...] 用方括号括起来的字符集中的字符来匹配输入,以找到最长的非空字符串;char *。在末尾添加'\0'。格式[]...]表示字符集中包含字符]
[^...] 用不在方括号里的字符集中的字符来匹配输入,以找到最长的非空字符串;char *。在末尾添加'\0'。格式[]...]表示字符集中包含字符]
% 字面值%,不进行赋值
字段类型字符:
如果变元是指向short类型而不是int类型的指针,那么在d/i/n/o/u/x这几个转换字符前可以加上字符h;
如果变元是指向long类型的指针,那么在d/i/n/o/u/x这几个转换字符前可以加上字符l;
如果变元是指向double类型而不是float类型的指针,那么在e/f/g这几个转换字符前可以加上字符l;
如果变元是指向long double类型的指针,那么在e/f/g前可以加上字符L。
(2) scanf
int scanf(const char *format, …);
scanf(...)等价于fscanf(stdin, ...)。
(3)sscanf
#include <stdio.h>
int sscanf(const char *buf, const char *format, …);
与scanf()基本相同,但sscanf()从buf指向的数组中读,而不是stdin。
4、字符输入输出函数
(1) fgetc
#include <stdio.h>
int fgetc(FILE *stream);
以unsigned char类型返回输入流stream中下一个字符(转换为int类型)。如果到达文件末尾或发生错误,则返回EOF。
(2) fgets
#include <stdio.h>
char *fgets(char *str, int num, FILE *stream);
返回:成功返回str,到达文件尾或发生错误返回NULL
从流stream中读入最多num-1个字符到数组str中。当遇到换行符时,把换行符保留在str中,读入不再进行。数组str以'\0'结尾。
(3) fputc
#include <stdio.h>
int fputc(int ch, FILE *stream);
返回:成功为所写的字符,出错为EOF
把字符ch(转换为unsigned char类型)输出到流stream中。
(4) fputs
#include <stdio.h>
int fputs(const char *str, FILE *stream);
返回:成功返回非负值,失败返回EOF
把字符串str输出到流stream中,不输出终止符'\0'。
(5) getc
#include <stdio.h>
int getc(FILE *stream);
getc()与fgetc()等价。不同之处为:当getc函数被定义为宏时,它可能多次计算stream的值。
(6) getchar
#include <stdio.h>
int getchar(void);
等价于getc(stdin)。
(7) gets
#include <stdio.h>
char *gets(char *str);
返回:成功为str,到达文件尾或发生错误则为NULL
从stdin中读入下一个字符串到数组str中,并把读入的换行符替换为字符'\0'。
gets()可读入无限多字节,所以要保证str有足够的空间,防止溢出。
(8) putc
#include <stdio.h>
int putc(int ch, FILE *stream);
putc()与fputc()等价。不同之处为:当putc函数被定义为宏时,它可能多次计算stream的值。
(9) putchar
#include <stdio.h>
int putchar(int ch);
等价于putc(ch, stdout)。
(10) puts
#include <stdio.h>
int puts(const char *str);
返回:成功返回非负值,出错返回EOF
把字符串str和一个换行符输出到stdout。
(11)ungetc
#include <stdio.h>
int ungetc(int ch, FILE *stream);
返回:成功时为ch,出错为EOF
把字符ch(转换为unsigned char类型)写回到流stream中,下次对该流进行读操作时,将返回该字符。对每个流只保证能写回一个字符(有些实现支持回退多个字符),且此字符不能是EOF。
5、直接输入输出函数
(1) fread
#include <stdio.h>
size_t fread(void *buf, size_t size, size_t count, FILE *stream);
返回:实际读入的对象数
从流stream中读入最多count个长度为size个字节的对象,放到buf指向的数组中。
返回值可能小于指定读入数,原因可能是出错,也可能是到达文件尾。实际执行状态可用feof()或ferror()确定。
(2) fwrite
#include <stdio.h>
size_t fwrite(const void *buf, size_t size, size_t count, FILE *stream);
返回:实际输出的对象数
把buf指向的数组中count个长度为size的对象输出到流stream中,并返回被输出的对象数。如果发生错误,则返回一个小于count的值。
6、文件定位函数
(1) fseek
#include <stdio.h>
int fseek(FILE *stream, long int offset, int origin);
返回:成功为0,出错为非0
对流stream相关的文件定位,随后的读写操作将从新位置开始。
对于二进制文件,此位置被定位在由origin开始的offset个字符处。origin的值可能为SEEK_SET(文件开始处)、SEEK_CUR(当前位置)或SEEK_END(文件结束处)。
对于文本流,offset心须为0,或者是由函数ftell()返回的值(此时origin的值必须是SEEK_SET)。
(2)ftell
#include <stdio.h>
long int ftell(FILE *stream);
返回与流stream相关的文件的当前位置。出错时返回-1L。
(3) rewind
#include <stdio.h>
void rewind(FILE *stream);
rewind(fp)等价于fssek(fp, 0L, SEEK_SET)与clearerr(fp)这两个函数顺序执行的效果,即把与流stream相关的文件的当前位置移到开始处,同时清除与该流相关的文件尾标志和错误标志。
(4) fgetpos
#include <stdio.h>
int fgetpos(FILE *stream, fpos_t *position);
返回:成功返回0,失败返回非0
把流stream的当前位置记录在*position中,供随后的fsetpos()调用时使用。
(5) fsetpos
#include <stdio.h>
int fsetpos(FILE *stream, const fpos_t *position);
返回:成功返回0,失败返回非0
把流stream的位置定位到*position中记录的位置。*position的值是之前调用fgetpos()记录下来的。
7、错误处理函数
当发生错误或到达文件末尾时,标准库中的许多函数将设置状态指示符。这些状态指示符可被显式地设置和测试。另外,(定义在<errno.h>中的)整数表达式errno可包含一个出错序号,该数将进一步给出最近一次出错的信息。
(1) clearerr
#include <stdio.h>
void clearerr(FILE *stream);
清除与流stream相关的文件结束指示符和错误指示符。
(2)feof
#include <stdio.h>
int feof(FILE *stream);
返回:到达文件尾时返回非0值,否则返回0
与流stream相关的文件结束指示符被设置时,函数返回一个非0值。
(3) ferror
#include <stdio.h>
int ferror(FILE *stream);
返回:无错返回0,有错返回非0
与流stream相关的文件出错指示符被设置时,函数返回一个非0值。
(4)perror
#include <stdio.h>
void perror(const char *str);
perror(s)用于输出字符串s以及与全局变量errno中的整数值相对应的出错信息,具体出错信息的内容依赖于实现。该函数的功能类似于:
fprintf(stderr, "%s: %s\n", s, "出错信息");