zoukankan      html  css  js  c++  java
  • 系统编程--标准IO

    1.流和FILE对象
      对于国际字符集,一个字符可以由一个以上的字节来表示。标准I/O文件流可以用来操作单字节和多字节(宽,wide)字符集。一个流的方向(orientation)决定了字符是以单字节还是多字节的方式读取,当一个流被创建时,它没有方向。如一个多字节I/O函数(见<wchar.h>)用在了没有方向的流上,那么流的方向会设为面向宽字符的。如果一个字节I/O函数用在一个没有方向的流上,那么流的方向会设为面向字节的。只有两个函数可以在设置后改变这个方向。freopen函数(简单说明)将会清除一个流的方向,而 fwide函数用来设置一个流的方向。

    #include <stdio.h>
    #include <wchar.h>
    int fwide(FILE *fp, int mode);
    //流面向宽字符返回正值、面向字节返回负值、没有方向则返回0。

    根据mode参数的不同值,fwdie函数执行不同的任务:
    1).如果mode参数为负值,fwide将会尝试让流面向字节;
    2).如果mode参数为正值,fwide将会尝试让流面向宽字符;
    3).如果mode参数为0,fwid将不会设置方向,而是返回一个表示流的方向的值。
    注意fwide不会改变一个已经有方向的流的方向。同样注意它不会返回错误。
    当打开一个流时,标准I/O函数fopen返回一个指向一个FILE对象的指针。这个对象通常是一个包含所有标准I/O库管理流所需的所有信息的结构体。

    2.缓冲
    三种缓冲类型:
    1).全缓冲:在这种情况下,当标准I/O的缓冲被填满后,真实的I/O才会发生。在磁盘上的文件通常都被标准I/O库完全缓冲。这个被使用的缓冲通常被标准I/O库在第一次操作一个流时通过调用malloc得到.注:Stdin和stdout如果被重定位到文件上,则是完全缓冲
    术语“冲洗(flush)”描述了标准I/O缓冲的写操作。一个缓冲可以被标准I/O函数自动冲洗,比如在缓冲满时,或者我们可以调用函数fflush来冲洗一个流。不幸的是,在UNIX环境里,冲洗有两个意思。在标准I/O库里,它表示缓冲的内容写出,它可能是部分填充的。

    2).行缓冲:在这种情况下,标准I/O库在输入输出时碰到一个换行符时会执行I/O操作。这让我们可以一次输出单个字符(使用标准I/O的 fputc函数),因为我们知道只当我们完成了每行的写操作时真实I/O才会发生。行缓冲被典型用在终端的流上:比如标准输入、标准输出。
    行缓冲伴随两个警告。第一,标准I/O库用来收集各行的缓冲区大小是固定的,所以当我们在写一个换行符之前填满这个缓冲I/O可能会发生。第二、不管何时通过标准I/O库从a、一个未缓冲流或b、(需要从内核被请求数据的)一个行缓冲流请求一个输入时,所有行缓冲输出流都会被冲洗。b情况括号内的补充条件的原因是,请求的数据可能已经在缓冲里了,这时不再需要从内核中读取数据。显然,a情况下任何从未缓冲流的输入,都会要求从内核获取数据。

    3).无缓冲:标准I/O库不缓冲字符。例如,如果我们用标准I/O函数fputs写15个字符,我们会期望这15个字符尽快地输出,它很可能使用了3.8节的write函数。
    标准错误流通常是无缓冲的。这是为了使任何错误消息都能尽快地显示,而不管它们是否包含一个换行符。
    通过调用下面两个函数的其中一个来改变缓冲:

    #include <stdio.h>
    void setbuf(FILE *restrict fp, char *restrict buf);
    void setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
    //成功返回0,失败返回非0值。

    3.打开流
    1).下面三个函数打开一个标准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 filedes, const char *type);
    //成功返回文件指针,错误返回NULL。

    区别:

    a.fopen函数打开一个指定的文件。
    b.freopen函数在一个指定的流上打开一个指定的文件,并关闭之前打开的流。如果之前的流有方向,freopen会清除它.
    c.fdopen函数接受一个已有的文件描述符,它可以通过open、dup、dup2、fcntl、pipe、socket、socketpair或 accept函数得到

    2).一个打开的流可以调用fclose来关闭

    #include <stdio.h>
    int fclose(FILE *fp);
    //成功返回0,错误返回EOF

    任何缓冲的输出数据都在文件关闭前被冲洗。任何可能被缓冲的输入数据都会被舍弃。如果标准I/O库之前自动开辟了一个缓冲,这个缓冲会被释放

    4.读和写流
    打开一个流,可以从以下三种非格式化的I/O中选择:
    ● 一次一字符(Character-at-a-time)I/O。我们可以一次读写一个字符,而让标准I/O函数处理所有的缓冲事宜,如果流有缓冲的话。
    ● 一次一行(Line-at-a-time)I/O。如果我们读一次读写一行,我们使用fgets和fputs。每行都以一个换行符终止,而且当我们调用fgets时必须指定我们能处理的最大行长度。
    ● 直接(Direct)I/O。这种类型的I/O通过fread和fwrite函数支持。对于每个I/O操作,我们读写一些对象,而每个对象都有一个指定的尺寸。这两个函数经常用来处理二进制文件,那里我们每次操作读写一个结构。
    1).输入函数:
    下面三个函数允许我们一次读一个字符

    #include <stdio.h>
    int getc(FILE *fp);
    int fgetc(FILE *fp);
    int getchar(void);
    //三者成功都返回下一个字符,失败都返回EOF。

    函数getchar被定义为与getc(stdin)等价。前两个函数的区别是getc可以被作为一个宏而实现,而fgetc不能作为宏实现

    2).注意上面三个函数当碰到一个错误或到达文件尾时返回同一个值。为了区别这两者,我们必须调用ferror或feof

    #include <stdio.h>
    int ferror(FILE *fp);
    int feof(FILE *fp);
    //两者如果条件成立的话返回非0(真),否则返回0(假)。
    void clearerr(FILE *fp);

    在多数实现上,在FILE对象里为每个流维护了两个标志:

    a.一个错误标志
    b.一个文件结束标志
    两个标志都可以调用clearerr来清除。

    3).在从一个流读入后,我们可以调用ungetc来把字符放回去。

    #include <stdio.h>
    int ungetc(int c, FILE *fp);
    //成功返回c,否则返回EOF。

    放回的字符会被后续在流上的读操作以放回的相反顺序返回

    4).输出函数
    对应于每个之前讨论过的输入函数的输出输出有:

    #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可以作为一个宏实现(将被替换为IO_putc),但fputc不能作为一个宏实现

    5.每次一行I/O
    1).以下两个函数提供一次一行输入功能

    #include <stdio.h>
    char *fgets(char *restrict buf, int n, FILE* restrict fp);
    char *gets(char *buf);
    //两者成功返回buf,文件结束或错误返回NULL。

    两都都指明一个把行读入的缓冲区的地址。gets函数从标准输入中读,而fgets从指定的流中读取。

    2).一次一行输出由fputs和puts提供

    #include <stdio.h>
    int fputs(const char *restrict str, FILE *restrict fp);
    int puts(const char *str);
    //成功返回非负值,否则返回EOF。

    函数fputs把一个null终止的字符串写入到指定的流中,末尾的null字节没有被写,puts函数在标准输出写一个空字符终止的字符串,但不会把这个空字节写出。不同的是puts接着会把一个换行符写入到标准输出

    6.二进制I/O
    以下两个函数被用来执行二进制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);
    //两者都返回读或写的对象数。

    这些函数有两种普遍的使用方法:

    1)读写一个二进制数组。
    2)读写一个结构体。

    7.定位流
    有三种方式来定位一个标准I/O流
    1)

    #include <stdio.h>
    long ftell(FILE *fp);
    //如果成功返回当前文件位置指示器,错误返回-1L。
    int fseek(FILE *fp, long offset, int whence);
    //成功返回0,否则返回非0值。
    void rewind(FILE *fp);

    对于一个二进制文件,一个文件的位置指示器是从文件开始的字节来衡量的.

    a.ftell用于二进制文件,返回值是这个字节位置。
    b.为了用fseek定位一个二进制文件,我们必须指定一个字节偏移以及这个偏移如何被解释。whence的值和前面的lseek函数里的一样:SEEK_SET表示从文件开头开始;SEEK_CUR表示从当前文件位置开始;SEEK_END表示从文件末尾开始。
    c.使用rewind函数也可将一个流设置到文件起始位置

    2).ftello函数和ftell函数相同,而fseeko函数和fseek函数相同,除了偏移量的类型是off_t而不是long

    #include <stdio.h>
    off_t ftello(FILE* fp);
    //成功返回当前文件位置,否则返回(off_t)-1。
    int fseeko(File *fp, off_t offset, int whence);
    //成功返回0,失败返回非0.

    3).想移植到非UNIX系统上运行应用程序应该使用fgetpos和fsetpos

    #inlcude <stdio.h>
    int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
    int fsetpos(FILE *fp, const fpos_t *pos);
    //两者成功都返回0,失败返回非0值。

    fgetpos返回把文件位置指示器的值存储在由pos指向的对象里。这个值可以被之后的fsetpos调用使用,来重定位流的位置。

    8.格式化I/O
    1).格式化输出
    格式化输出由4个printf函数处理:

    #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, ...);
    //两者成功都返回在字节里存储的字符数量,编码错误返回负值。

    printf函数向标准输出写,fprintf向一个指定的流写,而sprintf把格式化的字符串放入到数组buf里。sprintf函数在数组末自动将上一个空字节,但这个空字节没有包含在返回值里。注意sprintf可能为溢出buf指向的缓冲区,这时调用者的执行来保证这个缓冲区足够大。

    下面4个printf家族的变体与前4个函数相似,只是参数列表(...)被arg代替。

    #include <stdarg.h>
    #include <stdio.h>
    int vprintf(const char *restrict format, va_list arg);
    int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg);
    //两者成功都返回输出的字符数,失败返回负值
    int vsprintf(char *restrict buf, const char *restrict format, va_list arg);
    int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg);
    //两都成功都返回数组存储的字符数,编码错误返回负数。

    2).格式化输入

    格式化输入由三个scanf函数处理

    #include <stdio.h>
    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, ...);
    //三者都返回被赋值的输入项的数目,输入出错或碰到文件结尾时返回EOF。

    scanf家族用来解析一个输入的字符串并转换成指定类型的变量。在格式后的参数包含需要用转换结果初绐化的变量的地址。

    和printf家族一样,scanf家族也支持使用可变参数列表的函数,它们由<stdarg.h>指定。

    #include <stdarg.h>
    #include <stdio.h>
    int vscanf(const char *restrict format, va_list arg);
    int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg);
    int vsscanf(const char *restrict buf, const char *restrict format, va_list arg);
    //三者都返回赋好值的输入项的数量,错误或转换前遇到文件结尾时返回EOF

    9.实现细节
    可以调用fileno来得到流的文件描述符

    #include <stdio.h>
    int fileno(FILE *fp);
    //返回相关联的文件描述符。

    10.临时文件

    ISO C标准定义了两个由标准I/O库提供的函数,用来协助创建临时文件

    #include <stdio.h>
    char *tmpnam(char *ptr);
    //返回指向唯一的路径名的指针。
    FILE *tmpfile(void);
    //成功返回文件指针,失败返回NULL

    tmpname函数返回一个不与任何已有文件相同的合法路径名的字符串

  • 相关阅读:
    AXIS2远程调用WebService示例(Eclipse+AXIS)
    JSONPath使用说明
    七台机器部署Hadoop2.6.5高可用集群
    因为错误关闭Selinux导致CentOS7启动失败(进度条卡死,图形界面加载卡死)
    CentOS7静默安装Oracle11g
    MapReduce几种提交方式
    SpringMVC源码阅读:过滤器
    Java封装Redis常用操作
    IPv6 地址分类
    内网 LAN IPv6 环境配置 H3C S5500 Huawei S5700
  • 原文地址:https://www.cnblogs.com/tla001/p/6523164.html
Copyright © 2011-2022 走看看