UNIX环境编程学习笔记(13)——文件I/O之标准I/O流

来源:互联网 发布:贵金属行情分析软件 编辑:程序博客网 时间:2024/05/16 19:37

1 标准 I/O 流

之前学习的都是不带缓冲的 I/O 操作函数,直接针对文件描述符的,每调用一次函数可能都会触发一次系统调用,单次调用可能比较快捷。但是,对于需要频繁进行 I/O 操作的程序,频繁触发系统调用产生的消耗太大。

标准 I/O 库提供了带缓冲的 I/O 操作函数,这些函数围绕着一种叫做流(stream)的东西进行。当使用标准 I/O 库打开或创建一个文件时,系统提供了一个流与这个文件相关联。通过流的读入和输出完成所需要的 I/O操作。

标准 I/O 库使用一个 FILE 结构来管理流所需要的所有信息,包括:用于实际 I/O 的文件描述符、指向用于该流的缓冲区的指针、缓冲区的长度、当前在缓冲区中的字符数以及出错标志等等。指向 FILE 对象的指针我们可以称为文件指针。

标准 I/O 库为每个进程预定义了三个流:标准输入、标准输出和标准出错。这三个标准 I/O 流通过预定义文件指针 stdin、stdout 和 stderr 加以引用。这个三个文件指针定义在头文件 <stdio.h> 中。

2 缓冲

标准 I/O 流提供了缓冲是为了尽可能减少使用 read 和 write 系统调用的次数。标准 I/O 库提供了三种类型的缓冲:

1. 全缓冲。在这种情况下,在填满标准 I/O 缓冲区之后才进行实际 I/O操作。对于驻留在磁盘上的文件通常是由标准 I/O 库实施全缓冲的。

2. 行缓冲。在这种情况下,当在输入和输出中遇到换行符时,标准 I/O库执行 I/O 操作。当流涉及一个终端时(例如标准输入和标准输出),通常使用行缓冲。

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

3 打开流

标准 I/O 库提供了 fopen 函数来打开标准 I/O 流,

#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
返回值:若成功则返回文件指针,若出错则返回NULL

参数 pathname 指定了文件路径;而参数 type 指定了对该 I/O 流的读、写方式,ISO C 规定 type 参数可以有 15 种不同的值,其分别如表 1所示,

表 1: 打开标准 I/O 流的 type 参数type说 明r 或 rb为读而打开w 或 wb把文件截短至 0 长,为写而打开a 或 ab添加;为在文件尾写而打开r+ 或 rb+ 或 r+b为读和写而打开w+ 或 wb+ 或 w+b把文件截短只 0 长,或为读和写而打开a+ 或 ab+ 或 a+b为在文件尾读和写而打开或创建

使用字符 b 作为 type 的一部分,是为了区分文本文件和二进制文件。但是对于 UNIX 系统来说,并不区分这两种文件,所有有没有字符 b 都是一样的。

除非流引用终端设备,否则按系统默认的情况,流被打开时是全缓冲的。若流引用终端设备,则该流是行缓冲的。

因为输入和输出都是同一个位置指针,所以当以读和写类型打开一个文件时(type 中 + 符号),具有下面限制:

• 如果中间没有 fflush、fseek、fsetpos 或 rewind,则在输出的后面不能直接跟随输入。

• 如果中间没有 fseek、fsetpos 或 rewind,或这一个输入操作没有达到文件尾端,则在输入操作之后不能直接跟随输出。

UNIX 系统还提供了另外两个函数用于打开标准 I/O 流,

#include <stdio.h>
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict f
FILE *fdopen(int filedes, const char *type);
这两个函数返回值:若成功则返回文件指针,若出错则返回NULL

freopen 函数在一个指定的流上打开指定的文件,如若该流已经打开,则先关闭该流。此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准出错。

fdopen 函数获取一个现有的文件描述符,并使一个标准 I/O 流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数返回的描述符。

4 读和写流

标准 I/O 库提供了三种不同类型的非格式化 I/O 来对流进行读、写操作。

1. 每次一个字符的 I/O:一次读或写一个字符。

2. 每次一行的 I/O:每次读或写一行,每行都以一个换行符终止。

3. 直接 I/O:每次读或写指定长度的数据。

4.1 每次一个字符的 I/O

每次一个字符的输入函数为,

#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
三个函数的返回值:若成功则返回下一个字符,若已达到文件结尾或出错则返回EOF

函数 getchar() 等价于 getc(stdin)。getc 和 fgetc 两个函数的区别是:getc可被实现为宏,而 fgetc 不能被实现为宏。

每次一个字符的输出函数为,

#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
三个函数的返回值:若成功则返回c,若出错则返回EOF

putchar(c) 等效于 putc(c, stdout),putc 函数可以被实现为宏,而 fputc 函数不能被实现为宏。

4.2 每次一行的 I/O

每次输入一行的输入函数,

#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
两个函数的返回值:若成功则返回buf,若已达到文件结尾或出错则返回NULL

gets 函数从标准输入读取,但是 gets 函数不推荐使用,因其未指定缓冲区大小,可能会造成缓冲区溢出。另外,gets 与 fgets 的另一个不同是,gets函数没有将换行符存入缓冲区中。

每次输出一行的输出函数,

#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
两个函数返回值:若成功则返回非负数,若出错则返回EOF。

fputs 和 puts 两个函数都是将以 null 符终止的字符串写入到特定的流中(puts 函数写入到标准输出流),终止符不输出。但是,puts 函数会自动在输出字符串之后,再输出一个换行符。

4.3 直接 I/O

直接 I/O 也被称为二进制 I/O、一次一个对象 I/O、面向记录的 I/O或面向结构的 I/O,因为这种 I/O 函数通常被用于二进制 I/O 操作,一次读写一个二进制数组或者一个结构。

#include <stdio.h>
size_t fread(viod *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);
两个函数的返回值:读或写的对象数

这两个函数的 ptr 参数表示保存输入数据或要输出数据的缓冲区指针,size参数指定每个对象的大小,而 nobj 参数指定对象个数。

下面给出两个例子,

1. 读或写一个二进制数组。例如,将一个浮点数组的第 2 ~ 5 个元素写至一个文件上,

float data[10];if (fwrite(&data[2], sizeof(float), 4, fp) != 4) {    printf("fwrite error");}

2. 读或写一个结构。

复制代码
struct {    short count;    long total;    char name[NAMESIZE];} item;if (fwrite(&item, sizeof(item), 1, fp) != 1) {    printf("fwrite error");}
复制代码

5 出错标志

在标准 I/O 流中通常维持了两个标志:

• 出错标志。

• 文件结束标志。

通过前面的几个输入函数的介绍,我们发现在读取输入时,对于达到文件结尾和读取出错,通过返回值无法来判断是哪种情况。为了区分这两个情况,我们可以通过 ferror 或 feof 来进行判断,

#include <stdio.h>
int ferror(FILE *fp);
返回值:如果文件出错标志被设置则返回非0值(真),否则返回0(假)

int feof(FILE *fp);
返回值:如果文件达到结尾则返回非0值(真),否则返回0(假)

调用 clearerr 函数可以清除这两个标志。

#include <stdio.h>
void clearerr(FILE *fp);

6 关闭流

调用 fclose 函数可以关闭一个打开的流。

#include <stdio.h>
int fclose(FILE *fp);
返回值:若成功则返回0,若出错则返回EOF

在流被关闭之前,系统会自动冲洗缓冲区中的输出数据,并丢弃缓冲区中的任何输入数据,然后释放缓冲区(如果存在的话)。

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

7 定位流

UNIX 系统提供三套定位标准 I/O 流的方法,

1. ftell 和 fseek 函数。

2. ftello 和 fseeko 函数。这两个函数是 Single UNIX Specification 引入的。

3. fgetpos 和 fsetpos 函数。这两个函数是 ISO C 引入的。

#include <stdio.h>
long ftell(FILE *fp);
返回值:若成功则返回当前文件位置,若出错则返回-1L

int fseek(FILE *fp, long offset, int whence);
返回值:若成功则返回0,若出错则返回非0值

void rewind(FILE *fp);

使用 rewind 函数可以将一个流设置到文件的起始位置。对于二进制文件,fseek 定位流时必须指定偏移量 offset,以及解释这个偏移量的方式 whence。whence 的值可以是:SEEK_SET 表示从文件的起始位置开始,SEEK_CUR 表示从当前文件位置开始,SEEK_END 表示从文件的尾端开始。

对于文本文件,fseek 函数的 whence 参数必须是SEEK_SET,而且 offset只能是两种值:0,或是对该文件调用 ftell 所返回的值。因为在文本文件中,文件的当前位置可能不是以简单的字节偏移量来度量的。

#include <stdio.h>
off_t ftello(FILE *fp);
返回值:若成功则返回当前文件位置,若出错则返回-1

int fseeko(FILE *fp, off_t offset, int whence);
返回值:若成功则返回0,若出错则返回非0值

C 标准的两个函数 fgetpos 和 fsetpos,

#include <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, cosnt fpos_t *pos);

(done)

阅读全文
0 0