UNIX环境高级编程(第5章 标准I/O库)

时间:2021-09-04 22:16:16

        UNIX系统文件I/O操作的函数,openreadwriteclose等,是不带缓冲的IO操作。标准I/O库提供的fopenfreadfwritefclose是带缓冲的,目的是尽可能减少使用readwrite调用的次数。

系统文件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种预先定义好的标准流:标准输入流、标准输出流、标准错误输出流。它们通过预定义的文件指针stdinstdoutstderr加以引用。


2文件与文件流

FILE *fopen(pathname, type)打开(或创建新的)一个文件,创建一个与之相关联的流,并返回指向该流的文件指针。

文件可分为文本文件和二进制文件。

文本文件是用来保存字符的,文件中的字节都是字符的某种编码(如ASCIIUTF-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:文件指针,若fpNULL,则将导致所有输出流被冲洗。

返回

若成功则返回0,出错则返回EOF

功能

强制冲洗一个流,使该流所有未写的数据都被传送至内核。

setbuf函数:打开或关闭缓冲

头文件

#include <stdio.h>

函数原型

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

参数

fp:文件指针

buf:长度为BUFSIZ的缓冲区

返回

若成功则返回0,出错则返回非0

功能

打开或关闭缓冲机制。当bufNULL时,关闭缓冲。


头文件

#include <stdio.h>

函数原型

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

参数

fp:文件指针

buf:缓冲区

mode:缓冲类型,包括:

    _IOFBF 全缓冲

    _IOLBF 行缓冲

    _IONBF 不带缓冲(忽略bufsize

size:缓冲区buf的长度

返回

若成功则返回0,出错则返回非0

功能

打开或关闭缓冲,可精确指定所需的缓冲类型。


4标准IO库函数

4.1打开流

fopen函数

头文件

#include <stdio.h>

函数原型

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

参数

pathname:文件路径名

type:

rrb:        为读而打开

wwb:       把文件截短至0长,或为写而创建

aab      添加;为在文件尾写而打开,或为写而创建

r+r+brb+为读和写而打开

w+w+bwb+:把文件截短至0长,或为读和写而打开

a+a+bab+:为在文件尾读和写而打开或创建

注释:

  字符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);

功能:fgetcstream指向的输入流中读取下一个字符,并且把它由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);

功能:行输入

fgetsstream指向的流中读取字符,读取字符的数目最多比n指定的数目少1,然后将字符写入到s指向的数组中。读入换行符或者文件结束之后,不再读取其他的字符。最后一个字符写入数组后立即写入一个空字符。

返回:成功返回s,失败返回空指针

fputs

原型:int fputs(const char *s, FILE *stream);

功能:行输出

fputss指向的字符串写入stream指向的流中,不写入结束的空字符。

返回:如果发生了写错误返回EOF;否则返回一个非负值

gets

原型:char *gets(char *s);

功能:getsstdin指向的输入流中读取若干个字符,并将其保存到s指向的数组中,直到遇到文件结束或者读取一个换行符。所有的换行符都被丢弃,在最后一个字符读到数组中之后立即写入一个空字符。

返回: 成功返回s,失败返回空指针。

puts

原型:int puts(const char *s)

功能:putss指向的字符串写到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小。

     如果sizenmemb为零,则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