Chinaunix首页 | 论坛 | 博客
  • 博客访问: 69829
  • 博文数量: 11
  • 博客积分: 610
  • 博客等级: 中士
  • 技术积分: 135
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-08 14:33
文章分类
文章存档

2012年(3)

2011年(1)

2010年(3)

2008年(4)

我的朋友

分类: C/C++

2010-06-24 18:50:24

标准I/O库(1)
 
很多系统上都实现了标准I/O库,所以它由ISO C标准说明。标准I/O库是带缓冲的,这是它与POSIX定义的文件I/O显著的区别。
 

流(stream)

POSIX定义的文件I/O是针对文件描述符的,而标准I/O库是围绕流(stream)进行的,当用标准I/O库打开一个文件,就是将一个流与此文件关联起来。
 
关于流的概念,我在网上找到如下资料:
1)(K&R 《The C Programming Language》P241)
引用:
A stream is a source or destination of data that may be associated with a disk or other peripheral.

中文版(徐宝文等译)翻译为:
引用:
流(stream)是与磁盘或其它外围设备关联的数据的源或目的地

2) ()
引用:
Streams are a portable way of reading and writingdata. They provide a flexible and efficient means of I/O. A Stream is afile or a physical device (e.g. printer or monitor) which ismanipulated with a pointer to the stream.

翻译为:
引用:
流是(表达)读写数据的一种可移植的方法,它为一般的I/O操作提供了灵活有效的手段。一个流是一个由指针操作的文件或者是一个物理设备,而这个指针正是指向了这个流。

3)(ISO/IEC《 ISO/IEC 9899:1999 (E) 》)
引用:
Input and output, whether to or from physical devicessuch as terminals and tape drives, or whether to or from filessupported on structured storage devices, are mapped into logical datastreams, whose properties are more uniform than their various inputsand outputs.

翻译为:
引用:
不管是交互于诸如终端和磁带驱动器之类的物理设备,还是存取于由结构化存储设备支撑的文件,输入和输出(信息)都被映射为逻辑数据流,而流的属性却远不是诸多输入输出属性的统一。

4)(Kenneth .A.Reek著,徐波译《C和指针(Pointers On C)》第十五章P229)
引用:
ANSIC进一步对I/O的概念进行了抽象。就C程序而言,所有的I/O操作只是简单地从程序移进或移出字节的事情。因此,毫不惊奇的是,这种字节流便被称为流(stream)。程序只需要关心创建正确的输出字节数据,以及正确地解释从输入读取的字节数据。特定I/O设备的细节对程序员是隐藏的。

5)(引自Sergio C. Carbone 《Learning C For Real》P33)
引用:
What is meant by the stream concept?
- A stream is a logical, device independent way of handling many peripheral devices.
- Most peripheral devices are different and would require special programming
techniques.
- Streams mask this difference from the programmer and allows the programmer to
handle most peripheral devices in the same way.
- Streams are buffered interfaces that are thought of as a series of characters read one
at a time.
- PCs are based on a stream architecture.

翻译为:
引用:
流的概念意味着什么呢?
--流是独立于设备之外而操纵外设一种逻辑手段。
--大多数外设都是互异的,所以(操纵)它们需要专门的编程技术
--流对程序员隐藏这些不同点,而准许他们以同样的方式来处理大多数外设。
--考虑到一连串的字符需要一次读一个,流(相当于)是具有缓冲作用的接口。
--个人计算机都是基于流架构的。

各大权威对流的说法有些不一致,我认为流代表数据的源或目的,是操作数据的一种逻辑抽象。
 
流的定向(stream's orientation)决定了所读、写的字符是单字节还是多字节的。有两个函数可以改变流的定向,freopen()清除一个流的定向,fwide()设置流的定向。

     int fwide(FILE *fp, int mode);

 
打开一个流时,标准I/O函数fopen()返回一个指向FILE对象的指针,FILE是一个结构体,其定义是(摘自TC2.0中stdio.h文件):

/* Definition of the control structure for streams
*/

typedef struct {
       short level; /* fill/empty level of buffer */
       unsigned flags; /* File status flags */
       char fd; /* File descriptor */
       unsigned char hold; /* Ungetc char if no buffer */
       short bsize; /* Buffer size */
       unsigned char *buffer; /* Data transfer buffer */
       unsigned char *curp; /* Current active pointer */
       unsigned istemp; /* Temporary file indicator */
       short token; /* Used for validity checking */
} FILE; /* This is the FILE object */


 
   将它称为流控制结构体(control structure forstreams)真好表现出其功能来。举个例子就好像一卡车司机要把货物运到X公司,公司主管就会给他一张地图及X公司的基本信息,这些材料所提供的信息如果足够的话,那么它就能指导着司机准确地将货物送达了。C中FILE这个结构体所起的作用就好像是运输公司把一切有用的指导信息封装起来的档案袋一样。而已有关联的流要终止这种关联,就必须关闭流,使用的函数是fclose(),就像运货公司若不再给X公司运货了,那么他们就必须要终止合作协议了。 
   
关于C语言流(stream)的具体细节可参看:
a) K&R 《The C Programming Language》
b) Kenneth .A.Reek著,徐波译《C和指针(Pointers On C)》
c) Stevens的《UNIX环境高级编程》
d) 《ISO/IEC 9899:1999 (E)》及ANSI C手册

 

 

缓冲(buffering)

标准I / O提供了三种类型的缓存:
(1) 全缓存。 在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。对于驻留磁盘上的文件通常是由标准I/O库实施全缓存的。在一个流上执行第一次I/O操作时,相关标准I/O函数通常调用malloc(见7.8节)获得需使用的缓存。
术语冲洗(flush)说明标准I/O缓存的写操作。缓存可由标准I/O例程自动地冲洗(例如当填满一个缓存时),或者可以调用函数fflush刷新一个流。值得引起注意的是在UNIX环境中,刷新有两种意思。在标准I /O库方面,刷新意味着将缓存中的内容写到磁盘上(该缓存可以只是局部填写的)。在终端驱动程序方面(例如在第11章中所述的t c f l u sh函数),刷新表示丢弃已存在缓存中的数据。


(2) 行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准I / O库执行I /O操作。这允许我们一次输出一个字符(用标准I/O fputc函数),但只有在写了一行之后才进行实际I /O操作。当流涉及一个终端时(例如标准输入和标准输出),典型地使用行缓存。对于行缓存有两个限制。第一个是:因为标准I /O库用来收集每一行的缓存的长度是固定的,所以只要填满了缓存,那么即使还没有写一个新行符,也进行I /O操作。第二个是:任何时候只要通过标准输入输出库要求从( a )一个不带缓存的流,或者( b)一个行缓存的流(它预先要求从内核得到数据)得到输入数据,那么就会造成刷新所有行缓存输出流。在( b )中带了一
个在括号中的说明的理由是,所需的数据可能已在该缓存中,它并不要求内核在需要该数据时才进行该操作。很明显,从不带缓存的一个流中进行输入( ( a )项)要求当时从内核得到数据。


(3) 不带缓存。标准I / O库不对字符进行缓存。如果用标准I / O函数写若干字符到不带缓存的流中,则相当于用w r i t e系统调用函数将这些字符写至相关联的打开文件上。标准出错流s t d e r r通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新行字符。


ISO C要求下列缓冲特征:
(1) 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。
(2) 标准出错决不会是全缓冲的。


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

下面两个函数更改缓冲类型:

void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

强制冲洗一个流:

int fflush(FILE *fp);


打开流

下列三个函数打开一个标准I/O流:

#include <stdio.h>

FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);

FILE *fdopen(int filedes, const char *type);

关闭一个打开的流:

int fclose(FILE *fp);


读写流

一次读一个字符

int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void); //相当于getc(stdin)

int putc(int c, FILE *fp);

int fputc(int c, FILE *fp);

int putchar(int c);

出错处理

int ferror(FILE *fp);
int feof(FILE *fp);
void clearer(FILE *fp);

回送一个字符

int ungetc(char c, FILE *fp);

每次一行I/O

char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf); //可能造成缓冲区溢出,不建议使用
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str); //会自动输出一个换行符,不建议使用


阅读(658) | 评论(0) | 转发(0) |
0

上一篇:LINUX日志系统

下一篇:枚举值的陷阱

给主人留下些什么吧!~~