UNIX系统文件I/O操作的函数,open、read、write、close等,是不带缓冲的IO操作。标准I/O库提供的fopen、fread、fwrite、fclose是带缓冲的,目的是尽可能减少使用read和write调用的次数。
系统文件I/O函数都是针对文件描述符的。而标准I/O库函数都是针对流的,并通过FILE对象指针(即文件指针)来引用该流。
1 流
1.1)流的定义:
(1)一站式学习C编程
流是程序输入或输出的一个连续的字节序列,可用于单字节或多字节字符集,设备(如鼠标、键盘、屏幕、磁盘)的输入和输出都是用流来处理的。所有流均以文件的形式出现。
(2)K&R
流(stream)是与磁盘或其他外围设备相关联的数据的源或目的。
(3)
流是(表达)读写数据的一种可移植的方法,它为一般的I/O操作提供了灵活有效的手段。一个流是一个由指针操作的文件或者一个物理设备,而这个指针正是指向了这个流。
(4)C和指针
ANSIC进一步对I/O的概念进行了抽象。就C程序而言,所有的I/O操作只是简单地从程序移进或移出字节的事情。因此,毫不惊奇的是,这种字节流便被称为流(stream)。程序只需要关心创建正确的输出字节数据,以及正确地解释从输入读取的字节数据。特定I/O设备的细节对程序员是隐藏的。
(5)C标准库
不管是读出合适写入物理设备,例如终端,或者不管是读出还是写入到结构化存储设备支持的文件,输入和输出都和逻辑数据流相对应,这些逻辑数据流的属性比各种各样的输入输出更加统一。
1.2)概括:
流是对各种各样的输入输出的抽象,流的属性也比各种各样的输入输出更加统一。
对一个进程提供了3种预先定义好的标准流:标准输入流、标准输出流、标准错误输出流。它们通过预定义的文件指针stdin、stdout、stderr加以引用。
2文件与文件流
FILE *fopen(pathname, type)打开(或创建新的)一个文件,创建一个与之相关联的流,并返回指向该流的文件指针。
文件可分为文本文件和二进制文件。
文本文件是用来保存字符的,文件中的字节都是字符的某种编码(如ASCII或UTF-8),可以用cat查看,用vi编辑,如源文件就是文本文件。
二进制文件不是用来保存字符的,如目标文件、可执行文件、库文件就是二进制文件。文件中的字节表示其他含义,如可执行文件中有些字节表示指令,有些字节表示各section在文件中的位置,有些字节表示各segment的加载地址。
文件流可分为文本流和二进制流。
文本流是组成文本行的有序字符序列,每一行都由零个或多个字符加上一个标志结束的换行符组成。
二进制流是字符的有序序列,它可以透明地记录内部数据。在相同的实现下,从一个二进制流读入的数据应该和之前写入到这个流的数据相同。
3缓冲
标准I/O提供了三种类型的缓冲:
(1)全缓冲
这种情况下,在填满标准I/O缓冲区后才进行实际I/O操作。
(2)行缓冲
这种情况下,当再输入和输出中遇到换行符时,标准I/O库执行I/O操作。这允许我们一次输出一个字符(用标准I/O fputc函数),但只有在写了一行之后才进行实际I/O操作。
(3)不带缓冲
标准I/O库不对字符进行缓冲存储。
很多系统默认使用下面类型的缓冲:
(1)标准出错是不带缓冲的。
(2)若是涉及终端设备的其他流,则它们是行缓冲的;否则是全缓冲的。
下面代码是对各个标准I/O流打印缓冲状态信息:
#include "apue.h"
void pr_stdio(const char *name, FILE *fp)
{
printf("stream = %s, ", name);
if (fp->_IO_file_flags & _IO_UNBUFFERED)
printf("unbuffered");
else if (fp->_IO_file_flags & _IO_LINE_BUF)
printf("line buffered");
else
printf("full buffered");
printf(", buffer size = %d\n", fp->_IO_buf_end - fp->_IO_buf_base);
}
int main(void)
{
FILE *fp;
fputs("enter any charactr\n", stdout);
if (getchar() == EOF)
err_sys("getchar error");
fputs("one line to standard error\n", stderr);
pr_stdio("stdin", stdin);
pr_stdio("stdout", stdout);
pr_stdio("stderr", stderr);
if ((fp = fopen("/etc/hosts", "r")) == NULL)
err_sys("fopen error\n");
if (fgetc(fp) == EOF)
err_sys("getc error");
pr_stdio("/etc/motd", fp);
exit(0);
}
对应的输出信息:
[root]# ./a.out
enter any charactr
one line to standard error
stream = stdin, line buffered, buffer size = 4096
stream = stdout, line buffered, buffer size = 4096
stream = stderr, unbuffered, buffer size = 1
stream = /etc/motd, full buffered, buffer size = 4096
三个流重定向:
[root]# ./a.out < /etc/termcap >std.out 2> std.err
[root]# cat std.err
one line to standard error
[root]# cat std.out
enter any charactr
stream = stdin, full buffered, buffer size = 4096
stream = stdout, full buffered, buffer size = 16384
stream = stderr, unbuffered, buffer size = 1
stream = /etc/motd, full buffered, buffer size = 4096
fflush函数:冲洗一个流
头文件 | #include <stdio.h> |
函数原型 | int fflush(FILE *fp) |
参数 | fp:文件指针,若fp为NULL,则将导致所有输出流被冲洗。 |
返回 | 若成功则返回0,出错则返回EOF |
功能 | 强制冲洗一个流,使该流所有未写的数据都被传送至内核。 |
setbuf函数:打开或关闭缓冲
头文件 | #include <stdio.h> |
函数原型 | void setbuf(FILE *restrict fp, char *restrict buf); |
参数 | fp:文件指针 buf:长度为BUFSIZ的缓冲区 |
返回 | 若成功则返回0,出错则返回非0 |
功能 | 打开或关闭缓冲机制。当buf为NULL时,关闭缓冲。 |
头文件 | #include <stdio.h> |
函数原型 | void setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size); |
参数 | fp:文件指针 buf:缓冲区 mode:缓冲类型,包括: _IOFBF 全缓冲 _IOLBF 行缓冲 _IONBF 不带缓冲(忽略buf和size) size:缓冲区buf的长度 |
返回 | 若成功则返回0,出错则返回非0 |
功能 | 打开或关闭缓冲,可精确指定所需的缓冲类型。 |
4标准IO库函数
4.1 打开流
fopen函数
头文件 | #include <stdio.h> |
函数原型 | FILE *fopen(const char *restrict pathname, const char *restrict type); |
参数 | pathname:文件路径名 type: r或rb: 为读而打开 w或wb: 把文件截短至0长,或为写而创建 a或ab: 添加;为在文件尾写而打开,或为写而创建 r+或r+b或rb+: 为读和写而打开 w+或w+b或wb+:把文件截短至0长,或为读和写而打开 a+或a+b或ab+:为在文件尾读和写而打开或创建 注释: 字符b:表示打开二进制文件 字符+:表示文件为可读写 |
返回 | 若成功则返回文件指针,出错则返回NULL |
功能 | 打开一个标准I/O流 |
4.2 关闭流
fclose函数
头文件 | #include <stdio.h> |
函数原型 | FILE *fclose(FILE *fp); |
参数 | fp:文件指针 |
返回 | 若成功则返回0,出错返回EOF |
功能 | 关闭一个打开的文件流。在关闭文件之前,冲洗缓冲区中的输出数据,丢弃缓冲区中的任何输入数据。如果标准I/O库已经为该流自动分配一个缓冲区,则释放此缓冲区。 |
4.3 读写流:非格式化I/O
对打开的流进行读、写操作,有三种不同类型的非格式化I/O来选择:
1)每次一个字符的I/O。
fgetc | 原型:int fgetc(FILE *stream); 功能: fgetc从stream指向的输入流中读取下一个字符,并且把它由unsigned char类型转换为int类型,并且流的相关的文件定位符向前移动一位。 返回:返回stream指向的输入流中的下一个字符。 如果输入流在文件结束出,则设在文件结束符,返回EOF。 如果发生了读错误,则设置流的错误指示符,返回EOF。 |
fputc | 原型:int fputc(int c, FILE *stream); 功能:把c指定的字符(转换为unsigned char类型)写到stream指向的输出流中相关的文件定位符指定的位置处,并把文件定位符向前移动适当的位置。 返回:成功返回写入的字符;失败返回EOF |
ungetc | 原型:int ungetc(int c, FILE *stream); 功能:把c指定的字符(转换为unsigned char类型)退回到stream指向的输入流 中。 返回:成功返回转换后的回退字符,失败返回EOF |
getc | 原型:int getc(FILE *stream); 功能:等价于fgetc,是宏 返回:返回stream指向的输入流中的下一个字符。 如果到达文件尾或出现错误,返回EOF |
putc | 原型:int putc(int c, FILE *fp); 功能:等价于fputc,是宏 返回:成功返回写入的字符,失败返回EOF |
getchar | 原型:int getchar(void); 功能:等价于用参数stdin调用的getc。 #define getchar() getc(stdin) 返回:成功返回stdin指向的输入流的下一个字符。失败返回EOF |
putchar | 原型:int putchar(int c); 功能:等价于用stdout作为第二个参数调用的putc。#define putchar(c) putc((c), stdout) 返回:成功返回写入的字符,失败返回EOF |
2)每次一行的I/O。
fgets | 原型:char *fgets(char *s, int n, FILE *stream); 功能:行输入 fgets从stream指向的流中读取字符,读取字符的数目最多比n指定的数目少1,然后将字符写入到s指向的数组中。读入换行符或者文件结束之后,不再读取其他的字符。最后一个字符写入数组后立即写入一个空字符。 返回:成功返回s,失败返回空指针 |
fputs | 原型:int fputs(const char *s, FILE *stream); 功能:行输出 fputs把s指向的字符串写入stream指向的流中,不写入结束的空字符。 返回:如果发生了写错误返回EOF;否则返回一个非负值 |
gets | 原型:char *gets(char *s); 功能:gets从stdin指向的输入流中读取若干个字符,并将其保存到s指向的数组中,直到遇到文件结束或者读取一个换行符。所有的换行符都被丢弃,在最后一个字符读到数组中之后立即写入一个空字符。 返回: 成功返回s,失败返回空指针。 |
puts | 原型:int puts(const char *s) 功能:puts把s指向的字符串写到stdout指向的流中,并且在输出最后添加一个换行符。不写入结束的空字符。 返回:如果发生写错误返回EOF,否则返回一个非负值。 |
3)直接I/O。
fread | 原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 功能:从stream指向的流中读取最多nmemb个元素到ptr指向的数组中,nmemb的大小是由size指定的。 返回:成功返回读取的元素的数目。 读错误或文件结束,返回值可能比nmemb小。 如果size或nmemb为零,则fread返回0 |
fwrite | 原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 功能:从ptr指向的数组中读取最多nmemb个元素并将其写到stream指向的流中,nmemb的大小是由size指定的。 返回:成功返回写入的元素的数目。 写错误,返回值可能比nmemb小。 |
4.4 定位流
fseek | 原型:int fseek(FILE *stream, long int offset, int whence); 功能:为stream指向的流设置文件定位符。
返回:错误返回非零值 |
ftell | 原型:long int ftell(FILE *stream); 功能:获得stream指向的流的文件定位符的当前值。 返回:成功返回文件定位符的当前值,失败返回-1 |
feof | 原型:int feof(FILE *stream); 功能:测试stream指向的流的文件结束符 返回:当且仅当stream流设置了文件结束符时,feof返回一个非零值。 |
ferror | 原型:int ferror(FILE *stream); 功能:测试stream指向的流的错误指示符 返回:当前仅当stream流设置了错误指示符,ferror返回非零值 |
perror | 原型:void perror(const char *s); 功能:把整数表达式errno中的错误编号转换为一条错误信息。 返回:void |
4.5 格式化I/O
fprintf | 原型:int fprintf(FILE *stream, const char *format, …); 功能:格式化输出到stream指定的流 返回:成功返回输出的字符数目,失败返回负值 |
fscanf | 原型:int fscanf(FILE *stream, const char *format, …); 功能:格式化输入到stream指定的流 返回:成返回输入的字符数目,失败返回EOF |
sprintf | 原型:int sprintf(char *string, char *format, argc1,arg2, …); 功能:按照format格式格式化参数序列arg1,arg2,…,并将输出结果存放到数组string中,而不是标准输出。 返回:返回写到数组中的字符的数目,不包括终止的空字符。 |
sscanf | 原型:int sscanf(char *string, char *format, arg1, arg2, …); 功能:按照format格式扫描字符串string(而不是标准输入),并把结果分别保存到arg1,arg2,…这些参数上。 返回:成功返回输入项赋值的项目,失败返回EOF |