C-I/O操作函数详解
EOF: End Of File, 文字流结尾, 这里的文字流可以是文件(file), 也可以是标准输入(stdin), 它的值在任何可能出现的字符之外(-1)
先列出三种基本类型操作函数
这里面返回类型为int的基本失败后都返回EOF类型, 返回类型为指针的都返回NULL类型
1.
参数: mode表示流是用于只读、只写以及既读又写, 以及它是文本流还是二进制流, 无论(w, a)数据只能从文件的尾部写入(读可以选位置, 写只能从末尾), 如果使用附加模试("a"或"a+"), 任何写入文件数据都会被附加上去, 而文件的位置将会被忽略(用a模式打开后设置指针位置, 执行写入仍是在末尾添加) , 如果用"a+"表示该文件打开用于更新, 并且流既允许读也允许写, 但是如果你已经从该文件读了一些数据, 那么在你开始向它写入数据之前, 你必须调用其中一个文件定位函数(fseek, fsetpos, rewind), 在你向文件写入了一些数据之后, 如果你又想从该文件读取一些数据, 你首先必须调用fflush函数或者文件定位函数
FILE *fopen(char const *filename, char const *mode);打开一个特定的文件, 并把一个流和这个文件相关联
#include <stdio.h> int main(int arc, char **argv) { char *fname = "a.txt"; FILE *fp = fopen(fname, 'r'); if (fp == NULL) { perror(fname); } else { fclose(fp); } }
2.
FILE *freopen(char const *filename, char const *mode, FILE *stream);用于打开或者重新打开一个特定的文件流, 最后一个stream就是要打开的流, 可以是fopen函数返回的流, 也可能是标准刘stdin, stdout, stderr, 这个函数首先试图关闭这个流, 然后用指定的文件和模式重新打开这个流, 成功返回第三个参数值, 失败返回NULL
3.
int fclose(FILE *fp); 关闭流, 对于输出流, fclose函数在文件关闭之前刷新缓冲区, 如果执行成功返回零值, 否则返回EOF
4.当一个流被打开后可以用于输入和输出, 最简单形式是字符I/O操作, 字符输入是由getchar函数家族执行的
int getchar(void); //始终从标准输入读取
int getc(FILE *stream);
int fgetc(FILE *stream);
以上各个函数都从流中读取下一个字符, 并把它作为函数的返回值返回, 如果没有下个字符, 返回常量值EOF
注意: fgetc为真正的函数, getc, getchar为#define指令定义的宏, 返回的是int类型而不是char类型, 因为int型允许EOF, 假如返回char, 无法表示出EOF, 所以EOF的值必须在任何可能出现的字符范围之外
5.把单个字符写入到流中可以使用putchar函数家族
int putchar(int ch); //始终写入到标准输出流
int putc(int ch, FILE *stream);
int fputc(int ch, FILE *stream);
打印前, 函数会把这个整型参数裁剪为无符号字符类型, 所以putchar("abc")只会打印一个字符, 具体哪个字符看编译器
注意:fputc为真正的函数, putc, putchar为#define指令定义的宏, 函数失败返回EOF
6.
在读取字符的时候并不知道流的下一个字符是什么, 因此可能你所读取的字符是你要读取字符的后面一个字符, 但是不希望丢弃这个字符怎么办呢?用ungetc函数
int ungetc(int ch, FILE *stream); //把一个字符返回到流中, 类似PHP中unshift函数, 在最头部插入, 比如输入缓冲区为"abcd", 执行ungetc('z', stdin), 则输入缓冲区为"zabcd"
7.非格式化行I/O操作函数
char *gets(char *buffer);
char *fgets(char *buffer, int buffer_size, FILE *stream);
以上函数从流中读取字符, 并把他们复制到buffer中, 当读取到一个换行符并存储到缓冲区之后就不再读取, 如果缓冲区内存储的字符数达到buffer_size - 1个时它也停止读取, 一个NULL字节将被添加到缓冲区所存储数据的末尾, 使它成为一个字符串, 如果没有读取就到达了文件尾, 缓冲区则未修改, 此情况返回一个NULL指针, 否则返回FILE, 这个返回值通常只用于检查是否到达了文件尾
gets与fgets不同的是当gets读取一行输入时, 它并不在缓冲区中存储结尾的换行符
int puts(char const *buffer);
int fputs(char const *buffer, FILE *stream);
buffer必须是一个字符串, 它的字符被写入到流中, 这个字符串预期以NULL结尾, 所以函数没有缓冲区长度参数, 也就是说fgets类函数每次读取一行, 而fputs却既可以一次写入一行, 也可以写入多行, 如果写入时出现错误就返回EOF, 否则返回一个非负值
puts与fputs不同的是当puts写入一行字符串时, 它在字符串写入之后向输出再添加一个换行符
8.格式化行I/O操作函数
int scanf(char const *format, ...); 从标准输入流中读取
int sscanf(char const *string, char const *format, ...); 从字符串中读取
int fscanf(FILE *stream, char const *format, ...); 从给定的流中读取
当格式化字符串(format)达到末尾或者读取的输入不在匹配格式字符串所指定的类型的时候输入就停止, 任何一种情况下都返回被转换的输入值的数目, 如果在输入值被转换之前文件就到达尾部, 函数就返回常量值EOF
为了让这些函数正常运行, 指针参数的类型必须是对应格式代码的正确类型!函数无法验证它们的指针参数是否为正确的类型, 所以函数就假定它们是正确的, 如果指针参数类型不正确, 那么结果就是垃圾, 也就是说根据format的类型来运行指针所指向的值
除了%c以外(包括%s)的说明符会自动跳过输入项之前的空格, 而%c不会跳过任何字符
输入多个整数时, 可用空格或回车作为分隔符, 输入多个字符、整数与字符混合时, 不能用空格做为分隔符
scanf("%[*][宽度length][限定符u|h|l][格式化代码]", ...);
或scanf("%[A-Z]", str);或scanf("%[ABC]", str), 比如scanf("%50[^\n]", str); 这样可以输入带空格的字符串
由此可见format主要由四部分组成
*: 读指定类型的数据但不保存 scanf("%d %*c %d", &x, &y);
宽度: 只截取输入length个字符做转换, 没给出宽度函数就连续读入字符知道遇见输入中的空白字符 scanf("%2d%4d", &a, &b);输入"12345678"则a为12, b为3456
限定符: l, h等说明这是长整型或短整型或double类型等, 方便移植
格式化代码: c, s, d, x等
注意:
没有精度控制 比如scanf("%2.2f", &f);是非法的
C编译在碰到空格, TAB, 回车或非法数据(如对"%d"输入"12A"时, A即为非法数据)时即认为该数据(一个变量)结束
如果缓冲区里有数据就不会提示用户输入了!感受下面的例子
#include <stdio.h> int main(int argc, char **argv) { int a = 0; int b = 0; int c = 0; int ret = 0; ret = scanf("%d%d%d", &a, &b, &c); printf("第一次读入数量:%d\n", ret); ret = scanf("%d%d%d", &a, &b, &c); printf("第二次读入数量:%d\n", ret); return 0; }
造成这种原因的问题是缓冲区, 仔细分析下有好处!可以判断输入成功的熟练和清空缓冲区来解决!
9.printf家族函数
int fprintf(FILE *stream, char const *format, ...); //将结果送到指定流
int printf(char const *format); //结果输出送到标准输出
int sprintf(char *buffer, char const *format, ...); //存储到指定的buffer缓冲区中, 而不是写入到流中