标准I/O库

来源:互联网 发布:java jdk版本查看 编辑:程序博客网 时间:2024/05/01 12:31

标准I/O库处理很多细节,例如缓冲区分配,以优化长度执行I/O等。这些处理使用户不必担心如何选择使用正确的块长度。这使得它便于用户使用,但是如果不较深入地了解I/O库函数的操作,也会带来一些问题。


流和FILE对象

标准I/O文件流可用于单字节或多字节字符集。流的定向决定了所读、写的字符是单字节还是多字节流的。当一个流最初被创建时,它并没有定向。如若在未定向的流上使用一个多字节I/O函数,则将该流的定向设置为宽定向的。

若在未定向的流上使用一个单字节I/O函数,则将该流的定向设置为字节定向的。只有两个函数可改变流的定向。freopen函数清除一个流的定向;fwide函数设置流的定向。

#include <stdio.h>

#include <wchar.h>

int fwide(FILE *fp, int mode);

若流是宽定向的则返回正值,若流是字节定向的则返回负值,或者若流是未定向的则返回0。

根据mode参数的不同值,fwide函数执行不同的工作:

如若mode参数值为负,fwide将试图使指定的流是字节定向的。

如若mode参数值为正,fwide将试图使指定的流是宽定向的。

如若mode参数值为0,fwide将不试图设置流的定向,但返回标识该流定向的值。

fwide并不改变已定向流的定向,且无出错返回,可以在调用fwide前清除errno,fwide返回时检测errno的值。


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

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


缓冲

标准I/O库提供缓冲的目的是尽可能减少使用read和write调用的次数。它也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。

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

(1)全缓冲。这种情况下,在填满标准I/O缓冲区后才进行实际I/O操作。对于驻留在磁盘上的文件通常是由标准I/O库实施全缓冲的。在一个流上执行第一次I/O操作时,相关标准I/O函数通常调用malloc获得需使用的缓冲区。

术语冲洗(flush)说明标准I/O缓冲区的写操作。缓冲区可由标准I/O例程自动冲洗,或者可以调用fflush函数冲洗一个流。

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

对于行缓冲有两个限制。第一,因为标准I/O库用来收集每一行的缓冲区的长度是固定的,所以只要填满了缓冲区,那么即使还没有写一个换行符,也进行I/O操作。

第二,任何时候只要通过标准I/O库要求从一个不带缓冲的流,或者一个行缓冲的流得到输入数据,那么就会造成冲洗所有行缓冲输出流。

(3)不带缓冲。标准I/O库不对字符进行缓冲存储。标准出错流stderr通常是不带缓冲的。


可以调用下列两个函数中的一个更改缓冲类型

#include <stdio.h>

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

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

setbuf函数的参数buf必须指向一个长度为BUFSIZE的缓冲区,通常在此之后该流就是全缓冲的。将buf设置为NULL可以关闭缓冲。

setvbuf函数的参数mode可以取_IOFBF、_IOLBF、_IONBF。

如果指定一个不带缓冲的流,可以忽略buf和size参数。如果指定全缓冲或行缓冲,而buf是NULL,则标准I/O库将自动地为该流分配适当长度的缓冲区。


任何时候,都可强制冲洗一个流。

#include <stdio.h>

int fflush(FILE *fp);

如若fp是NULL,则此函数将导致所有输出流被冲洗。


打开流

#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);

type参数指定对该I/O流的读、写方式

type                                      说明

r或rb                                     为读而打开

w或wb                                  把文件截短至0长,或为写而创建

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

r+或r+b或rb+                       为读和写而打开

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

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


调用fclose关闭一个打开的流

#include <stdio.h>

int fclose(FILE *fp);

在该文件被关闭之前,冲洗缓冲区中的输出数据。丢弃缓冲区中的任何输入数据。如果标准I/O库已经为该流自动分配了一个缓冲区,则释放次缓冲区。

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


读和写流

1.输入函数

以下三个函数可用于一次读一个字符。

#include <stdio.h>

int getc(FILE *fp);

int fgetc(FILE *fp);

int getchar(void);

不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,必须调用ferror或feof。

int ferror(FILE *fp);

int feof(FILE *fp);

void clearerr(FILE *fp);

每个流在FILE对象中维持了出错标志和文件结束标志,调用clearerr则清除这两个标志。

从流中读取数据以后,可以调用ungetc将字符再压送回流中。

int ungetc(int c, FILE *fp);

若成功则返回c,若出错则返回EOF。


2.输出函数

每个输入函数都有一个输出函数。

int putc(int c, FILE *fp);

int fputc(int c, FILE *fp);

int putchar(int c);


每次一行的I/O

#includee <stdio.h>

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

char *gets(char *buf);

gets从标准输入读,而fgets则从指定的流读。

fgets需指定缓冲区的长度n。如果一行的数据大于n-1个字符时,fgets返回一个不完整的行,对fgets的下一次调用会继续读该行。

gets容易造成缓冲区溢出,且不将换行符存入缓冲区中。


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

int puts(const char *str);

fputs函数将一个以null结尾的字符串写到指定的流,它并不要求在null之前一定是换行符。

puts函数将一个以null结尾的字符串写到标准输出后,又将一个换行符写到标准输出。


二进制I/O

#include <stdio.h>

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

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

两个函数都返回读或写的对象数。

对于读,如果出错或到达文件尾端,则此数字可以少于nobj。在这种情况下,应调用ferror或feof进行判断。对于写,如果返回值少于所要求的nobj,则出错。


定位流

#include <stdio.h>

long ftell(FILE *fp);

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

void rewind(FILE *fp);


格式化I/O

1.格式化输出

#include <stdio.h>

int printf(const char *restrict format, ...);

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

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

int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);

2.格式化输入

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

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

int sscanf(const char *restrict buf, const char *restrict format, ...);


实现细节

在unix系统中,标准I/O库最终都要调用文件I/O。每个标准I/O流都有一个与其相关联的文件描述符,可以对一个流调用fileno函数以获得其描述符。

#include <stdio.h>

int fileno(FILE *fp);


临时文件

#include <stdio.h>

char *tmpnam(char *ptr);

FILE *tmpfile(void);

tmpnam函数产生一个与现有文件名不同的一个有效路径名字字符串。每次调用它时,它都产生一个不同的路径名。

若ptr是NULL,则所产生的路径名存放在一个静态区中,下一次再调用tmpnam时,会重写该静态区。

tmpfile函数创建一个临时二进制文件,在关闭该文件或程序结束时将自动删除这种文件。

0 0
原创粉丝点击