Unix标准IO库相关函数总结之读写流(二)
读写流的三种方式
一旦一个流被打开,则可以选择三种方式对其进行读写。
- 每次一个字符的IO,一次读或者写一个字符,如果流是带缓冲的,则标准IO函数处理所有的缓冲。
- 每次一行的IO。每次读写一行数据,可以使用fgets和fputs函数,但是要说明最大行的长度。
- 直接IO。通常使用fread和fwrite函数。
一个字符的IO
输入函数
通常使用以下三个函数进行一个字符的读。
1 2 3 4
| #include <stdio.h> int getc(FILE*); int fgetc(FILE*); int getchar();
|
区别与联系
这三个函数都用于一个字符的读取。其区别和联系如下:
- getchar()相当于getc(stdin)。即每次从标准输入流读入一个字符。实质上getc是宏。
- getc和fgetc的区别是getc可以被实现为宏,而fgetc不能被实现为宏。这就意味着:
- getc的参数不能是具有副作用的表达式,因为它的值可能被计算多次。
- fgetc是一个函数,可以获得其地址, 这就允许将fgetc作为参数传递给另一个函数。
- 调用fgetc时间比getc时间长,因为调用宏的时间更短。
有副作用的表达式是指:表达式的作用本质是用于计算的,原则上只返回一个计算结果,而不会改变表达式中的变量的值。这种不改变表达式中变量值的表达式叫做无副作用的表达式。如:x+y,y-z等。除此之外,若表达式中变量的值被改变则成为有副作用的表达式,如x++,y+=2;
由于在宏中宏可能出现在程序的很多位置,也就是表达式会被计算多次,这个时候若表达式有副作用就会GG。
返回值
这三个函数的返回值都是int类型,这三个函数在返回下一个字符的时候,将其unsigned char类型转换为int。说明无符号的理由是,如果最高位为1,也不会使返回值为负。返回整形的理由是这样既可以返回所有的字符,也可以返回出错或到达文件为的指示值。
注意不管是到达文件为还是出错,这个时候三个函数的返回值都一样。为了区分这两种情况,常调用ferror或feof函数。
1 2 3 4 5 6 7
| #include <stdio.h> int ferror(FILE* fp); int feof(FILE* fp); void clearerr(FILE* fp);
|
在大多数的实现中,为每个流在FILE对象中维护了两个标志:
调用clearerr可以清除这两个标志。
压送字符到流中。
从流中读取字符以后,可以使用ungetc将字符押送回流中,压回的字符又可以从流中读出,读出的顺序与压送的顺序相反。压送的字符不会被写到流中。
1 2
| #include <stdio.h> int ungetc(int c, FILE* fp);
|
###输出函数
输出函数为以下三个,与输入对应,区别与联系和输入函数相同。
1 2 3 4
| #include <stdio.h> int putc(int c, FILE* fp); int fputc(int c, FILE* fp); int putchar(int c);
|
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <stdlib.h> int main() { FILE* test = fopen("test.txt", "rw"); char a; while ((a =getc(test)) != EOF) { putc(a, stdout); putc('\n', stdout); } ungetc('1', test); ungetc('2', test); putc(getc(test), stdout); putc(getc(test), stdout); if (ferror(test)) { printf("this is the read error\n"); } if (feof(test)) { printf("this is the eof\n"); } fclose(test); }
|
一行的IO
输入
标准IO中提供了一下两个函数进行一行的读取:
1 2 3
| #include <stdio.h> char* fgets(char* restrict buf, int n, FILE* fp); char* gets(char* buf);
|
返回值
若读取成功返回buf,若读取失败或者读取到文件结尾返回NULL.
区别与联系
- 对于fgets必须指定缓冲区的长度n.此函数一直读到下一个换行符为止,但是不能超过n-1个字符,读入的字符将被送入缓冲区,该缓冲区以null结尾。若改行包含换行符超过了n-1个字符,fgets只返回一个不完整的行。下一次读取的时候将继续从该行继续往下读。
- gets函数用于从标准输入读取,但是gets不包含缓冲区的长度,所以在读取的时候可能会出现缓冲区溢出的情况。一般最好不要用gets函数.
- gets函数不将换行符存入缓冲区,而fgets将换行符存入缓冲区。
虽然ISO要求提供gets函数,但一般使用fgets不要使用gets函数。
输出
对应的,标准IO提供了以下连个函数进行输出:
1 2 3
| #include <stdio.h> int fputs(const char* restrict buf, FILE* restrict fp); int puts(const char* buf);
|
返回值
成功返回非负值,失败返回EOF。
区别
- fputs将一个以null结尾的字符串写到指定的流,但是null不写出,注意这不是每次输出一行,而取决于缓冲区中的内容。
- puts将一个以null结尾的字符串写到指定的流,null不写出,但是在最后又添加了一个换行符。
- 一般情况下使用fgets和fputs,不适用gets和puts函数。
实例
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h> int main() { FILE* fp = fopen("test.txt", "rw"); char buf[5]; while (fgets(buf, 5, fp)) { printf("%s", buf); } return 0; }
|
直接IO
直接IO通常用于对二进制文件的读写,除此之外也可以对文本文件进行读写。其中对二进制文件的读写只能用直接IO的方式,因为二进制文件中可能含有null字符,会导致使用行或者字符获取时出现错误。直接IO进行读写的两个函数如下:
1 2 3 4
| #include <stdio.h> size_t fread(void* restrict ptr, size_t size, size_t nobj, FILE* fp); size_t fwrite(const void* restrict ptr, size_t size, size_t nobj, FILE* fp);
|
参数说明
- ptr:为缓冲区指针
- size:为一个结构体(类型)的大小
- nobj:结构体的个数,若在fread中说明将要写入nobj个大小为size的结构体(类型),若为fwrite,则为要从流fp中读取nobj个大小为size的数据
- fp:文件指针
返回值
返回值为实际读取或写入的对象的个数。对于fread,若文件出错或者读到文件结尾处都可以少于nobj,对于fwrite,若返回值小于nobj,则写入出错。
注意
使用这两个函数存在一个问题就是,他们只能用于读写在同一个系统上已写的数据。若是通过网络挂载的文件则不可行。
fread,fwrie可移植,而read,write不可移植。
格式化输出
格式化输出有以下几个函数:
1 2 3 4 5 6 7
| #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, ...);
|
返回值
前三个函数若输出成功,则返回输出字符的个数;若输出出错,则返回负值。
sprintf和snprintf若执行成功,则返回存入buf中的字符串的长度,否则返回负值。
函数说明
- printf:向标准输出输出字符串。
- fprintf:向标准文件流输fp出字符串。
- dprintf:向文件描述符所指向的文件输出字符串。
- sprintf:向缓冲区buf写入字符串。
- snprintf:安全的向缓冲区buf写入字符串并指定缓冲区的最大长度.一般用此函数代替sprintf
1 2
| %[flags][fldwidth][precision][lenmodifier]converter
|
- flags : 是该输出的标志,其包含如下几个值:
- ‘ : 将整数按千位分组字符。
-
-
- (空格) : 如果第一个字符不是正负号,则在其前面加上一个空格
: 指定另一种转换形式,如对于16进制,在前面加0x
- 0 : 添加前导0进行填充
- fldwidth : 说明最小字段宽度,若参数字段小于此宽度,多余位置用空格填充。
- precision : 浮点数精度。前导为.
注意:fldwidth可以用*作为占位符,然后在后面对其进行指定。
1 2 3 4 5 6 7 8
| #include <stdio.h> int main () { printf("%10.2lf", 10.1); printf("%*.*lf", 10, 2, 10.1); }
|
Comment and share