设为首页 加入收藏

TOP

深入理解C语言----标准I/O小结(缓冲区,I/O函数及其他相关问题)(一)
2014-11-23 19:48:26 来源: 作者: 【 】 浏览:17
Tags:深入 理解 语言 ---- 标准 I/O 小结 缓冲区 函数 及其他 相关 问题

与文件I/O围绕文件描述符操作不同,标准I/O的操作是围绕流进行的。

流:

对于流,《C和指针》里有一段解释得很好:

ANSI C进一步对I/O的概念进行了抽象。就C程序而言,所有的I/O操作只是简单地从程序移进或移出字节的事情。因此,毫不惊奇的是,这种字节流便被称为流(stream)。程序只需要关心创建正确的输出字节数据,以及正确地解释从输入读取的字节数据。特定I/O设备的细节对程序员是隐藏的。

TCPL Appendix B.1中这么解释:

A stream is a source or destination of data that may be associated with a disk or other peripheral.(流是一个可能与硬盘或者其他设备关联的数据的源或者目的地)

简单地说,流是对信息的一种抽象。C系统在处理文件(文本文件和二进制文件)时,并不区分类型,都看成是字符流,按字节进行处理。
输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。

流有最小的信息单元就是二进制位,含有最小的信息包就是字节,C标准库提供两种类型的流:二进制流(binary stream)和文本流(text stream)。二进制流是有未经处理的字节构成的序列;文本流是由文本行(每行有0个或多个字符,并以'\n'结束)组成的序列。注意在UNIX中,并不区别两种流。


当一个程序启动时,,标准输入、输出、出错三个流就已经被自动打开,并对应到默认的物理终端。这三个标准I/O流通过预定义(stdio.h)文件指针stdin,stdout,stderr加以引用当一个进程正常终止时(直接调用exit(),或从main返回)所有打开的标准I/O流都会被关闭,所有带未写缓冲数据的I/O流都会被冲洗。

PS:在main()中return(expr)等价于exit(expr),而exit则调用fclose()关闭每个文件描述符并刷洗对应缓存。

在Linux的应用程序中,通常用文件描述符0,1,2与标准输入,标准输出,标准出错输出相关联。为符合POSIX规范,在 中,0,1,2分别被替换成常量符号STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。


缓冲区:

(详细内容可以参考APUE3.9和5.4,本段纯属摘抄)

标准I/O提供缓存的目的是尽可能减少使用read和write调用的数量(系统调用比普通函数调用开销大)。它也对每个I/O流自动地进行缓存管理,避免了应用程序需要考虑这一点所带来的麻烦。

标准I/O提供了三种类型的缓存:
(1) 全缓存。在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。对于驻在磁盘上的文件通常是由标准I/O库实施全缓存的。
(2) 行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准I/O库执行I/O操作。这允许我们一次输出一个字符(用标准I/O fputc函数),但只有在写了一行之后才进行实际I/O操作。
(3) 不带缓存。标准I/O库不对字符进行缓存。如果用标准I/O函数写若干字符到不带缓存的流中,则相当于用write系统调用函数将这些字符写至相关联的打开文件上。


标准出错流stderr通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新行字符。
ANSI C要求下列缓存特征:
(1) 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。
(2) 标准出错决不会是全缓存的

但是,这并没有告诉我们如果标准输入和输出涉及交互作用设备时,它们是不带缓存的还是行缓存的,以及标准输出是不带缓存的,还是行缓存的。
SVR4和4.3 + BSD的系统默认使用下列类型的缓存:
标准出错是不带缓存的。
如若是涉及终端设备的其他流,则它们是行缓存的;否则是全缓存的。


可以通过下面的函数改变缓存类型(APUE5.4):

void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
这些函数必须在流打开之后、但是未对流做任何操作之前被调用

参数buf通常指向一个长度为BUFSIZ的缓冲区,BUFSIZ在stdio.h中定义,可自行输出查看

stdio.h:	#ifndef BUFSIZ
		# define BUFSIZ _IO_BUFSIZ
libio.h:	#define _IO_BUFSIZ _G_BUFSIZ
_G_config.h:	#define _G_BUFSIZ 8192
如要关闭缓冲,将buf置为NULL

Liunx上的默认情况是,当标准输入输出连接终端时是行缓冲的,缓冲区大小1024字节,重定向到普通文件时,他们变为全缓冲(APUE 5.12 程序5.3提供查看I/O相关信息的方法)


强制冲洗一个流

int fflush(FILE *fp)

使该流所有未写数据传送至内核。如fp为NULL,将使所有输出流被清洗。

应当注意的是:fflush(NULL)并不能有效地清空输入缓存。后面详细讨论


常用I/O函数:

流打开:

FILE * fopen ( const char * filename, const char * mode );
FILE * freopen ( const char * filename, const char * mode, FILE * stream );
常用freopen进行输入输出重定向。

单字符读写:

int getc(FILE *fp)
int fgetc(FILE *fp)
int getchar(void)
getchar等价于getc(stdin)。前两个函数区别在于,getc可被实现为宏,意味着fgetc调用时间略长。

不管是出错还是到达文件尾端,三个函数都返回-1.

在大多数实现中,FILE维护了两个标志:出错标志和文件结束标志。可用下面三个函数判断流是出错还是结束,最后一个函数是清除两标志:

int ferror(FILE *fp)
int foef(FILE *fp)
void clearerr(FILE *fp)

还有一个神奇的函数,可以把字符压送回流中:
int ungetc(int c, FILE *fp)
注意不能回送EOF

类似地,输出函数:

int putc(int c, FILE *fp)
int fputc(int c, FILE *fp)
int putchar(int c)
putchar(c)等价于putc(c,stdout),putc可被实现为宏。

通常为了避免过多的函数调用开销,putchar和getchar都被实现为宏。


行读写函数:

char * fgets ( char * str, int num, FILE * stream );
它会读取不超过num-1个字符,然后在末尾加上结束符 '\0' ,或者遇到换行符结束输入,同时换行符也被传入。

另一个函数:

char * gets(char* buf);

由于存在缓冲区溢出漏洞,不推荐使用。

相应地,输出

int fputs(const char * str, FILE * fp);
int puts(const char *char);
fputs()将一个以NULL终止的字符串写到指定的流,终止符NULL不写
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C语言总结――结构体(struct)使.. 下一篇语义分析:C语言表达式的语法树生..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: