Unix/Linux编程-标准I/O库

标准I/O库

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

进程中预定义了这3个流,可以自动地被进程调用。这些流引用的文件与文件描述符中的STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO所引用的相同。这3个标准I/O流通过预定义文件指针stdin、stdout和stderr加以引用,定义在头文件<stdio.h>中。

2.2 打开标准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 fd, const char *type);

返回值:成功返回文件指针,出错返回NULL


3个函数区别如下:
(1) fopen打开路径名为pathname的一个指定文件。
(2) freopen在一个指定的流(fp)上打开一个指定的文件,如若该流已经打开,则先关闭该流。若该流已经定向,则使用freopen清除该定向。一般用于将一个指定的文件打开为一个预定义的流:stdin,stdout,stderr。
(3) fdopen取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合。常用于创建管道和网络通信通道返回的描述符,因为这次特殊的文件不能用标准I/O函数打开。
type参数指定对该I/O流的读、写方式:

Type

说明

open(2)标志

r

只读打开

O_RDONLY

w

把文件长度截断0,写打开或创建

O_WRONLY|O_CREAT|O_TRUNC

a

在文件为写,或为写而创建

O_WRONLY|O_CREAT|O_APPEND

r+

为读写打开

O_RDWR

w+

文件长度截断0,或为读写而打开或创建

O_RDWR|O_CREAT|O_TRUNC

a+

在文件尾读和写而打开或创建

O_RDWR|O_CREAT|O_APPEND


对于fdopen而言,type参数的意义稍有区别,因为该描述符已经被打开,所以并不会截断该文件,真正决定是否截断该文件的是原来open的标志。
在指定w或a类型创建一个新文件时,我们无法说明该文件的访问权限位,而是由系统的umask值来限制这些权限。我们可以通过调整umask值来限制我们创建一个新文件的权限。

2.3 读和写流

2.3.1 每次一个字符的I/O

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

#include <stdio.h>

int getc(FILE *fp);

int fgetc(FILE *fp);

int getchar(void);

返回值:成功返回一个读取到的字符,若到文件尾或出错,返回EOF


getchar等同于getc(stdin);getc可以被实现为宏,而fgetc不能实现为宏。
不管是出错还是达到文件尾端,函数否返回EOF,为了区分这两种不同的情况,需要嗲用ferror或feof函数。

2.3.2 每次一行的I/O

#include <stdio.h>

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

char *gets(char *buf);

返回值:成功返回buf,达到文件尾端或出错返回NULL


gets从标准输入读取,fgets从指定流中读取n字节。
fgets函数一直读到下一个换行符为止,但是不超过n-1个字符。读到的字符被送入缓冲区buf中。该缓冲区以null字节结尾。如果改行包括最后一个换行符的字符数超过n-1,则fgets只返回一个不完整的行,但缓冲区总是以null字节结尾。
gets是一个不推荐使用的函数,有可能造成缓冲区溢出。

2.3.3 二进制I/O

因为fputs在遇到null字节时就停止,而在结构中可能包含有null字节,所以不能实现读结构的要求。同样输入数据中包含null字节,fgets也不能正确工作。因此,提供了下列两个函数以执行二进制I/O操作。

#include <stdio.h>

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

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

返回值:读或写的对象数

2.4 流的定位

#include <stdio.h>

long  ftell(FILE *fp);

返回值:成功返回当前文件的位置指示,出错返回-1L

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

返回值:成功返回0,出错返回-1

void rewind(FILE *fp);

fseek中的whence与lseek函数相同。分别是SEEK_SET、SEEK_CUR和SEEK_END。

2.5 关闭流

#include <stdio.h>

int  fclose(FILE *fp);

返回值:成功返回0,出错返回EOF

在该文件被关闭之前,冲洗缓冲中的输出数据。缓冲区中的任何输入数据被丢弃。如果标准I/O库已经为该流自动分配一个缓冲区,则释放此缓冲区。
当一个进程中止时(直接调用exit函数或main函数返回),则所有带未写缓冲数据的标准I/O流都被冲洗,所有打开的标准I/O流都被关闭。


2.6 缓冲

标准I/O库提供 缓冲的目的是尽可能减少使用read和write调用的次数。标准I/O提供了3种类型的缓冲。

2.6.1 全缓冲

全缓冲,在填满标准I/O缓冲区后才进行实际I/O操作。对于驻留在磁盘上的文件通常是有标准I/O库实施全缓冲的。
术语冲洗(flush)说明标准I/O缓冲区的写操作。缓冲器可由标准I/O例程自动冲洗,或者调用fflush冲洗一个流。

2.6.2 行缓冲

当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。虽然标准库允许我们一次输出一个字符,但只有在写了一行之后才进行实际I/O操作。当设计一个终端时,通常使用行缓冲。对于行缓冲有两个限制,第一,因为标准I/O库用来收集每一行的缓冲区的长度是固定的,所以只要填满了缓冲区,那么即使还没有写一个换行符,也进行I/O操作。第二,任何时候只要通过标准版I/O库从一个不带缓冲的流或者一个行缓冲的流得到输入数据,那么就会冲洗所有行缓冲输出流。

2.6.3 不缓冲

标准I/O库不对字符进行缓冲存储,字符会被立即输出。标准出错流strerr通常是不带缓冲的,这使得出错信息可以尽快显示出来,而不管他们是否含有一个换行符。
对任何一个给定的流,如果我们并不喜欢这些系统默认,可以调用下列两个函数中的一个更改缓冲类型:

#include <stdio.h>

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

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

返回值:成功返回0,出错返回非0

使用setbuf函数打开或关闭缓冲机制。为了带缓冲进行I/O,参数buf必须指向一个长度为BUFSIZE的缓冲区(该常量定定义在<stdio.h>中)。通常在此之后该流就是全缓冲,但如果该流与一个终端设备相关,那么可能将其设置为行缓冲。如果要关闭缓冲,将buf设置为NULL。
使用setvbuf函数,我们可以精确地说明所需的缓冲类型。这是用mode参数实现的:

mode

说明

_IOFBF

全缓冲

_IOLBF

行缓冲

_IONBF

不到缓冲

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

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

#include <stdio.h>

int fflush(FILE *fp);

返回值:成功返回0,出错返回EOF

2.7 内存流

标准I/O库吧数据缓存在内存中,因此每次一字符和每次一行的I/O更有效。我们也可以通过调用setbuf或setvbuf函数让I/O库使用我们自己的缓冲区。标注的I/O流,虽然仍使用FILE指针进行访问,但其实并没有底层文件,所有的I/O都是通过在缓冲区与主存之间来回传送字节完成的。

#include <stdio.h>

FILE *fmemopen(void *buf, size_t size, const char * type);

返回值:成功返回流指针,出错返回NULL

函数允许调用者提供缓冲区用于内存流;buf参数指向缓冲区的开始位置,size指定了缓冲区大小的字节数。如果bug参数为空,fmemopen函数分配size字节数的缓冲区,当流关闭时缓冲区会被释放。
type参数控制如何使用流:

type

说明

r

为读而打开

w

为写而打开

a

追加,为在第一个null字节处写而打开

r+

为读写而打开

w+

把文件截断0长度,为读写而打开

a+

追加:为在第一个null字节处读和写而打开


这些取值对应基于文件的标准I/O流的type参数值,但其中有些微笑差别。
(1) 无论何时以追加写方式打开内存时,当前位置设为缓冲区中的第一个null字节。如果缓冲区中不存在null字节,则当前位置就设为缓冲区结尾的后一个字节。当流不是以追加方式打开时,当前位置设为缓冲去的开始位置。因为追加写模式通过第一个null字节确定数据的尾端,内存留并不适合存储二进制数据(二进制数据在数据尾端之前就可能包含多个null字节)。
(2) 如果buf参数是一个null指针,打开流进行读写没有任何意义。
(3) 任何时候需要增加缓缓冲区中的数量以及调用fclose、fflush、fseek、fseeko和fsetpos时都会在当前位置写入一个null字节。

猜你喜欢

转载自blog.csdn.net/water_3700348/article/details/78246137