标准I/O

标准I/O是在系统调用基础上构造的;

所有C语言均有I/O,但可能在实现上各有不同;

标准I/O并不可靠,使用时要小心。

流和文件对象

在系统调用(低级I/O)时当打开一个文件时,即返回一个整数文件描述符,然后该文件描述符就用于后续的I/O操作。

对于标准I/O库,它们的操作则是围绕流(stream)进行的。当用标准I/O库打开或创建一个文件时,标准I/O函数fopen返回一个指向FILE对象的指针。该对象通常是一个结构,它包含了I/O库为管理该流所需要的所有信息:用于实际I/O的文件描述符,指向流缓存的指针,缓存的长度,当前在缓存中的字符数,出错标志等等。应用程序没有必要检验FILE对象。

象系统调用一样,为了引用一个流,需将FILE指针作为参数传递给每个标准I/O函数。

标准I/O流与系统V的STREAMSI/O系统不同。

标准输入、标准输出和标准出错

对一个进程预定义了三个流,它们自动地可为进程使用:标准输入、标准输出和标准出错。

文件描述符STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO分别表示它们。

这三个标准I/O流通过预定义文件指针stdin,stdout和stderr加以引用。

这三个文件指针同样定义在头文件<stdio.h>中。

缓存

标准I/O提供了三种类型的缓存:

(1) 全缓存。在这种情况下,当填满标准I/ 缓存后才进行实际I/ 操作。

(2) 行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准I/O库执行I/O操作。

(3) 不带缓存。标准I/O库不对字符进行缓存。如果用标准I/O函数写若干字符到不带缓存的流中,则相当于用write系统调用函数将这些字符写至相关联的打开文件上。

标准出错流stderr通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新行字符。 

缓存特征

ANSI C要求下列缓存特征:

(1) 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。

(2) 标准出错决不会是全缓存的。

UNIX缓冲的默认特征:

 标准出错是不带缓存的。

 如若是涉及终端设备的其他流,则它们是行缓存的,否则是全缓存的。 

更改缓存类型

对任何一个给定的流,可调用以下两个函数中的一个更改缓存类型:

#include <stdio.h>

#include <stdio.h>

void setbuf(FILE *fp, char *buf) ;

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

返回:若成功则为0,若出错则为非0。

mode

_IOFBF 全缓存

_IOLBF 行缓存

_IONBF 不带缓存

Setbuf和setvbuf的参数

强制刷新一个流:fflush()

功能:强制刷新一个流缓冲区;

方法:

# include <stdio.h>

int fflush(FILE  *fp) ;

返回:若成功则为0,若出错则为EOF。

此函数使该流所有未写的数据都被传递至内核。作为一种特殊情形,如若fp是NULL,则此函数刷新所有输出流。

流的打开: fopen()

功能:打开流;

方式:下列三个函数用于打开一个标准I / O流。

#include <stdio.h>

FILE *fopen(const char  *pathname, const char *type) ;

FILE *freopen(const char *path name, const char *type, FILE *fp) ;

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

返回值:

若成功则为文件指针,若出错则为NULL。

三个函数的区别

(1) fopen打开路径名由pathname 指示的一个文件。

(2) freopen在一个特定的流上(由f p指示)打开一个指定的文件(其路径名由pathname 指示),如若该流已经打开,则先关闭该流。此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准出错。

(3) fdopen取一个现存的文件描述符(我们可能从open , dup , dup2 , fcntl或pipe函数得到此文件描述符),并使一个标准的I / O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数获得的描述符。因为这些特殊类型的文件不能用标准I/O fopen函数打开,首先必须先调用设备专用函数以获得一个文件描述符,然后用f d o p e n使一个标准I / O流与该描述符相结合。

打开标准I / O流的type参数

 

打开标准I/O流的六种不同的方式

流的关闭: fclose()

功能:关闭一个标准流;

用法:

#include <stdio.h>

int fclose(FILE  * fp) ;

返回值:若成功则为0,若出错则为EOF。

说明:在文件被关闭之前,刷新缓存中的输出数据。

缓存中的输入数据被丢弃。

如果标准I / O库已经为该流自动分配了一个缓存,则释放此缓存。

当一个进程正常终止时(直接调用exit函数,或从main函数返回),则所有带未写缓存数据的标准I / O流都被刷新,所有打开的标准I / O流都被关闭。

I/O

一旦打开了流,则可在三种不同类型的非格式化I/O中进行选择,对其进行读、写操作。

(1) 每次一个字符的I/O。一次读或写一个字符,如果流是带缓存的,则标准I / O函数处理所有缓存。

(2) 每次一行的I/O。使用fgets和fputs一次读或写一行。每行都以一个新行符终止。 

(3) 直接I/O。fread和fwrite函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中读或写一个结构。 

字符输入函数

功能:从流中读入一个字符;

用法:

#include <stdio.h>

int getc(FILE  *fp) ;

int fgetc(FILE *fp) ;

int getchar(void);

返回值:

若成功则为一个字符,若已到文件尾端或出错则为EOF;

错误判断及清除

错误判断:

#include <stdio.h>

int ferror(FILE *fp) ;

int feof(FILE  *fp) ;

若条件为真则为非0,否则为0;

错误清除:

void clearerr(FILE  *fp) ;

字符回送:ungtec()

功能:将一个字符回送到流中。

用法:

#include <stdio.h>

int ungetc(int c, FILE *fp) ;

返回值:若成功则为c,若出错则为EOF.

说明:

送回到流中的字符以后又可从流中读出,但读出字符的顺序与送回的顺序相反。

回送的字符,不一定必须是上一次读到的字符。EOF不能回送。但是当已经到达文件尾端时,仍可以回送一字符。下次读将返回该字符,再次读则返回EOF。

当正在读一个输入流,并进行某种形式的分字或分记号操作时,会经常用到回送字符操作。

有时需要先看一看下一个字符,以决定如何处理当前字符。

字符输出函数

功能:向流中输出一个字符

用法:

#include <stdio.h>

int putc(int c , FILE *fp) ;

int fputc(int c, FILE *fp);

int putchar(int c) ;

返回值:若成功则为c,若出错则为EOF

行I/O

功能:行输入;

用法:

#include <stdio.h>

char *fgets(char  *buf,  int n,FILE  *fp) ;

char *gets(char  *buf) ;

返回值:

若成功则为buf,若已处文件尾端或出错则为NULL。

功能:行输出:

用法:

#include <stdio.h>

int fputs(const char *str,  FILE  *fp) ;

int puts(const char  *str) ;

返回值:

若成功则为非负值,若出错则为EOF。

二进制I/O

字符I/O只能用于文本文件,而对于二进制文件则会出错。对于二进制文件必须使用二进制I/O。

功能:以二进制方式进行I/O。

用法:

#include <stdio.h>

size_t fread(void  *ptr, size_t size, size_t nobj, FILE *fp) ;

size_t fwrite(const void  *ptr,  size_t size, size_t nobj, FILE  *fp) ;

返回:读或写的对象个数。 

流定位

有两种方法定位标准I / O流。

(1) ftell和fseek。这两个函数自SYS V7以来就存在了,但是它们都假定文件的位置可以存放在一个长整型中。

(2) fgetpos和fsetpos。这两个函数是新由ANSI C引入的。它们引进了一个新的抽象数据类型fpos_t,它记录文件的位置。在非UNIX系统中,这种数据类型可以定义为记录一个文件的位置所需的长度。

需要移植到非U N I X系统上运行的应用程序应当使用fgetpos和fsetpos。

 流定位,可参照系统调用lseek()。

ftell,fseek,rewind

ftell:

功能:确定文件位置。

用法:

#include <stdio.h>

long ftell(FILE  *fp) ;

返回:若成功则为当前文件位置指示,若出错则为-1L

fseek:

用法:int fseek(FILE *fp, long offset, int whence) ;

返回值:若成功则为0,若出错则为非0(-1)。

rewind:

功能:定位到文件首

用法:void rewind(FILE  *f p) ;

返回值:无

fgetpos, fsetpos

功能:获得或设置文件位置。

用法:

#include <stdio.h>

int fgetpos(FILE  *fp, fpos_t  *pos) ;

int fsetpos(FILE  *fp, const fpos_t  *pos) ;

返回值:

若成功则为0,若出错则为非0(-1). 

格式化I/O

有两类格式化/O:

 格式化输入 

 格式化输出 

传统格式化输出

功能:

格式化输出;

用法:

#include <stdio.h>

int printf(const char  *form a t, ...);

int fprintf(FILE  *fp, const char  *format, ...);

int sprintf(char *buf, const char  *format, ...);

返回值:

存入数组的字符数。

可变长度参数格式输出

功能:

可变长度参数格式化输出:

用法:

#include <stdarg.h>

#include <stdio.h>

int vprintf(const char  *format,  va_list arg) ;

int vfprintf(FILE  *fp, const char   *format, va_list arg) ;

int vsprintf(char  *buf, const char  *format, va_list arg) ;

返回值:

若成功则为输出字符数,若输出出错则为负值。 

传统格式化输入

功能:格式化输入。

用法:

#include <stdio.h>

int scanf(const char  *format, ...);

int fscanf(FILE *fp, const char  *format, ...);

int sscanf(const char *buf, const char  *forma t, ...);

返回值:指定的输入项数,若输入出错,或在任意变换前已至文件尾端则为EOF。

获得文件描述符

在系统调用中使用一个整数来描述文件,但在高级I/O中却使用FILE指针来描述文件。

在高级I/O内的FILE结构中,仍然包含有系统调用中使用的整数描述符。FILE结构中的整数描述符可能通过函数fileno( )得到。

功能:获得低级文件描述符。

用法:

#include <stdio.h>

int fileno(FILE  *fp) ;

返回:与流相关联的文件整数描述符。

说明:如果要使用dup()或fcntl()对文件进行控制时必须使用整数文件描述符。

临时文件

UNIX提供两个产生临时文件的函数:tmpnam( )和 tmpfile( )。用于临时文件的操作与使用。

tmpnap()

功能:产生临时文件名。

用法:

#include <stdio.h>

char *tmpnam(char  *ptr) ;

返回:指向唯一路径名的指针

tmpfile()

功能:打开一个二进制临时文件;

用法:

FILE *tmpfile(void);

返回:若成功则为文件指针,若出错则为NULL。

临时文件的相关说明

使用临时文件时,tmpnam和tmpfile配合使用,先由tmpanm创建文件名,再由tmpfile打开临时文件。

使用临时文件的一些限制在/usr/include/bits/stdio_lim.h中定义。相关值有:

L_tmpnam  临时文件名长度 

TMP_MAX  产生不重复临时文件的最大次数 

FILENAME_MAX tmpnam 的最大长度 

P_tmpdir  临时目录:/tmp

临时文件示例

#include	<stdio.h>
main(){
char	name[L_tmpnam], line[100];
FILE	*fp;
printf("%s\n",tmpnam(NULL)); 	 // 1st tmpnam
tmpnam(name);	 // 2nd tmpnam
printf("%s\n",name);	 // Disp 2nd tmpnam
if((fp=tmpfile())==NULL)	 // create temp file
perror(name);
fputs("One Line of Output!\n",fp);	// write to tmpfile
rewind(fp);	 //  to the Beginning
if(fgets(line,sizeof(line),fp)==NULL)	 perror("fgets");
fputs(line,stdout);
}


tempnam

tempnam是tmpnam的一个变体,它允许调用者为所产生文件名指定目录和前缀。

用法:

#include <stdio.h>

char *tempnam(const *dir, cost char *prefix);

 说明: 

(1)  如果定义了环境变量TMPDIR, 则用其作为目录。

(2)  如果参数dir 非NULL, 则用其作为目录。 

(3)  将<stdio.h > 中的字符串P_tmpdir 用作为目录。 

(4)  将本地目录,通常是/tmp , 用作为目录。 

 如果prefix 非NULL, 则它应该是最多包含5 个字符的字符串,用其作为文件名的头几个字符。 

关于标准I/O的说明

标准I/O库并不完善—某些属于基本设计,但是大多数则与各种不同的实现有关。

不同厂家的实现相关很大,可以比较我们的教材和Linux系统的实现。

标准I/O库使用了缓存机制,而这种机制是产生很多问题,引起很多混淆的一个领域。

标准I/O库不能很好的控制屏幕等设备,若想很好地使用格式化输出控制屏幕输出可以使用curses库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值