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


    lienhua34
    2014-09-29

    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)

  • 相关阅读:
    Web Services Software Factory tutorial (3 of 5)
    [转]张孟苏考上的不是大学
    试用期要盯紧你的“四金”
    Asp.net 随记 Part2 (31 49)
    如何查找当前页面元素---DataList
    Web Services Software Factory tutorial (2 of 5)
    SharePoint:扩展DVWP 第19部分:可维护的下拉框 填充关系列表(下)
    SharePoint:扩展DVWP 第14部分:用jQuery编写PreSaveAction()实务
    SharePoint:扩展DVWP 第16部分:为实现可维护的下拉框准备数据
    SharePoint:扩展DVWP 第5部分:在保存操作提交前做些事情——PreSaveAction()
  • 原文地址:https://www.cnblogs.com/lienhua34/p/4003194.html
Copyright © 2020-2023  润新知