Chinaunix首页 | 论坛 | 博客
  • 博客访问: 101443
  • 博文数量: 34
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 307
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-17 12:43
文章分类

全部博文(34)

文章存档

2011年(1)

2009年(5)

2008年(28)

我的朋友

分类: LINUX

2008-12-09 13:00:03

第五章 标准I/O

本章讲述ISO C的标准I/O库函数(定义在)。这些库函数的特点是会在进程的地址空间中开辟一个缓冲区,在一定条件下时才触发I/O(称为冲洗flush)将缓冲区的数据刷到内核中。

1、文件流和FILE指针

ISO C中使用文件流的概念描述和操作磁盘文件

在实现了宽字符集的C99中,流的定向决定了在流中以单字节还是多字节读写字符。可使用fwide函数进行设置:

  1. #include 

  2. int fwide(FILE *fp, int mode);

mode的取值为:

>0 宽字节定向

<0 字节定向

=0 不设置,返回当前定向方式

返回值:

>0 宽字节定向

<0 字节定向(需先结合errno判断是否发生函数调用失败)

=0 未定向

2stdinstdoutstderr

这是三个预定义的文件流指针,定义在

3、标准I/O的缓冲方式

全缓冲(fully buffered): 缓冲区满时触发I/O

行缓冲(line buffered): 遇到'\0'换行符或缓冲区满时触发I/O

非缓冲(unbuffered): 直接触发I/O

  • 函数fflush用于立即冲洗指定的标准输出流,以NULL为输入参数时表示冲洗全部输出流:

  1. #include 

  2. int fflush(FILE *fp);
  • 行缓冲常用于ttytelnet等交互式命令行终端,它们的特点是通过换行回车结束字符串输入,即这种方式输入的字符串都以'\n'+'\0'结尾。不涉及交互时通常是权缓冲的;

  • 当内核从行输入缓冲区取数时,将造成行输出缓冲马上被冲洗。一个例子是当前shell的后台作业执行结束时,只要键入(stdin)回车,作业完成的提示将马上出现在终端屏幕(stdout)上;

  • stderr的默认是非缓冲的方式输出的,ISO C规定stderr的输出不能为全缓冲;

  • 进程正常终止(显式或隐式的调用了exit)时,所有的缓冲区将马上被冲洗;

  • 设置缓冲方式的函数:

  1. #include 

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

要求: fp已打开后才能调用;

在对流执行任何一个其它操作之前调用;

setbuf用于显式的为fp指向的文件流指定缓冲区为buf,缓冲区的长度必须为BUFSIZ(定义在)。bufNULL时,表示关闭缓冲区;

setvbuf通过mode指定缓冲类型,并可通过size指定buf的长度(建议是BUFSIZ)。当bufNULL时,表示让系统来分配缓冲区;mode参数的选项包括了:_IOFBUF(全缓冲)、_IOLBUF(行缓冲)、_IONBUF(非缓冲);

4、对文件流进行打开、读写和关闭

  1. 打开

  1. #include 

  2. FILE *fopen(const char *restrict pathname, const char *restrict type);
  3. FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
  4. FILE *fdopen(int filedes, const char *type);

freopen用于将fp指向的流重定向到pathname上,类似dup2

fdopen不是ISO C,它根据一个已打开的文件描述符filedes创建指向文件流的FILE指针,它常用于将文件流指针关联到已打开的管道或套接字;

参数type是个字符串,包括了"r""w""a""r+""w+""a+"。对于type参数,应注意:

  • 在使用wa选项时,若文件不存在则自动创建,新建文件的访问模式通过进程的umask限制;

  • 后缀b(如rbwb、……等)对UNIX无意义。

  • 对于fdopen,选项w不能截文件为0,而由filedesopen函数决定;

  • r+w+方式打开文件时,输出/输入之后必须等待冲洗或者改变流位置才能进行输入/输出;

若引用的文件是交互式终端,则默认打开方式为行缓冲,否则默认是全缓冲;

  1. 关闭

  1. int fclose(FILE *fp);

close一样,进程结束时所有打开的文件流也被自动关闭;

  1. 读写

每次读/写一个字符到buffer

  1.  #include 

  2. /* 左边是读,右边是写 */
  3. int getc(FILE *fp); int putc(int c, FILE *fp);
  4. int fgetc(FILE, *fp); int fputc(int c, FILE *fp);
  5. int getchar(void); int putchar(int c);
  • getcputc通常实现为宏,而fgetcfputc不能实现为宏;

  • 这几种读/写函数之间主要区别为:参数表达式、函数地址(宏函数无地址)、效率;

  • 由于读函数在文件结束或出错时都将返回EOF,应通过以下两个函数返回值的真假作具体判断:

  1. #include 

  2. int ferror(FILE *fp);
  3. int feof(FILE *fp);
  • 函数clearerr则用于清除文件流的EOF标志:

  1. #include 

  2. void clearerr(FILE *fp);
  • 函数ungetc可将一个字符压(push)到流中:

  1. #include 

  2. int ungetc(int c, FILE *fp);

下次getc将再次读到这个字符,像栈的进出(push/pop)一样;

  • getcharputchar使用的文件流分别为stdinstdout


每次读/写一行字符串到buffer

  • 读(输入):

  1. #include 

  2. char *fgets(char *restrict buf, int n, FILE *restrict fp);
  3. char *gets(char *buf);

fgets从指定的流读取n个字符(或者读到一个'\0')到stdinbufferbuffer在满时往缓冲区的最后一格填充'\0'

gets函数带来缓冲区溢出(buffer overflow)的安全隐患,ISO C已经不推荐使用。现代的系统仍然将其实现只是为了向前兼容的考虑;

  • 写(输出):

  1. #include 

  2. int fputs(const char *restrict str, FILE *restrict fp);
  3. int puts(const char *str);

fputs将一个字符串送到指定的文件流,注意换行符'\n'需自己指定;

puts将指定的字符串送到stdout,并自动跟一个'\n'换行符(即总是换行,包括puts(""));


直接I/O(也称二进制I/O,面向结构的I/O等)

  1. #include 

  2. size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
  3. size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);

fread从文件流fp读取nobj个记录到ptr中,其中每个记录的长度为sizefwrite则从ptr中取nobj个记录写到fp指向的文件流中,其中每个记录的长度为size

返回值为实际读写的对象数。对于fread返回值小于nobj时应通过feofferror判断结果,而fwrite则肯定是写失败;

注意:由于字节对齐方式以及字节顺序因编译器或体系结构而异,freadfwrite容易造成二进制代码的不兼容;

5、文件流的定位

  1. #include 

  2. long ftell(FILE *fp);
  3. int fseek(FILE *fp, long offset, int whence);
  4. void rewind(FILE *fp);

ftell返回当前在文件流中的位置(以long为步长);

fseekwhence指定的起始位置,将当前位置重新定位在offset处,whence的含义同lseek

rewind复位当前位置到SEEK_SET

另有ftellofseekooff_t为步长,参数列表类似ftellfseek

  1. #include 

  2. int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
  3. int fsetpos(FILE *fp, const fpost_t *pos);

fgetpos将文件流的当前位置存到pos处,而fsetpos将当前位置设置为pos

6、文件流I/O的格式化函数

printf家族:格式化输出

  1. #include 

  2. int printf(const char *restrict format, ...);
  3. int fprintf(FILE *restrict fp, const char *restrict format, ...);
  4. int sprintf(char *restrict buf, const char *restrict format, ...);
  5. int snprintf(char *restrict buf, size_t n, const char *resctrict format, ...);

printf家族函数分别把指定的格式化字符串format输出到标准输出stdout/文件流fp/字符缓冲区buf,其中snprintf是为解决sprintf的缓冲区溢出隐患的替代函数;

可以使用的格式化标记可参考K&R编写的《The C Programming Language》一书,典型的使用包括%4d, %3.2f, %*.3f等;

vprintf(3)等前缀了个v的变体使用可变参数列表va_list替代格式化标记参数;

它们的返回值都是实际输出的字符数;

scanf家族:格式化输入

包括了scanf(3), fscanf(3), sscanf(3)及前缀了v使用va_list的变体,函数原型略;

使用时应注意输入必须和格式化的字符串匹配,否则第一个不匹配的字符后面的部分将被直接丢弃。

空白字符(空格、制表符等)均归为转义符'\s'

7、关联文件流到文件描述符(非ISO C

  1. #include 

  2. int fileno(FILE *fp);

fdopen互逆;

8、创建临时文件

  1. #include 

  2. char *tmpnam(char *ptr);
  3. FILE *tmpfile(void);

tmpnam创建了一个包含路径名的字符串存入prt,并返回其值。供其它函数创建临时文件,ptrNULL时,这个路径名保存在系统分配的静态区中,可能会被多次重写;

tmpfilewb+创建一个临时文件并返回其指针,在关闭该指针或者进程结束时这个临时文件将被自动删除;

SUSXSI扩展也提供了两个创建临时文件的函数:

  1. #include 

  2. char *tempnam(const char *directory, const char *prefix);

tempname在环境变量TMPDIR指定的目录下提供了一个以prefix指定为前缀的临时文件名,prefix只有效于最多前5个字符;

  1. #include 

  2. int mkstemp(char *template);

函数通过template生成一个临时文件,并打开之,返回其文件描述符。同时修改template为该文件的路径名。这个临时文件需要程序另行删除;

另有类似mkstemp的函数mktemp(2),对应的shell命令为mktemp(1),用于生成一个临时文件名,但不会创建之。

阅读(714) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~