zoukankan      html  css  js  c++  java
  • PILE读书笔记_标准I/O

    在学习和分析标准I/O库的同时, 可以重点与Linux的I/O系统调用进行比较。

    stdin、 stdout和stderr都是FILE类型的文件指针, 是由C库静态定义的, 直接与文件描述符0、 1和2相关联, 所以应用程序可以直接使用它们。其中,stdin是不可写的, stdout是不可读的, 而stderr不仅不可读, 且没有缓存。

    I/O的缓存

    C库的I/O接口对文件I/O进行了封装, 为了提高性能, 其引入了缓存机制, 共有三种缓存机制: 全缓存、 行缓存及无缓存。

    (1)全缓存一般用于访问真正的磁盘文件。 C库会为文件访问申请一块内存, 只有当文件内容将缓存填满或执行冲刷函数flush时, C库才会将缓存内容写入内核中。

    (2)行缓存一般用于访问终端。 当遇到一个换行符时, 就会引发真正的I/O操作。 需要注意的是, C库的行缓存也是固定大小的。 因此, 当缓存已满, 即使没有换行符时也会引发I/O操作。

    (3)无缓存, 顾名思义, C库没有进行任何的缓存。 任何C库的I/O调用都会引发实际的I/O操作。

    下面看一个行缓存的例子:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 int main(void)
     5 {
     6     printf("Hello ");
     7     if (0 == fork()) {
     8         printf("child
    ");
     9         return 0;
    10     }
    11     printf("parent
    ");
    12     return 0;
    13 }

    执行结果如下:

    之所以是这样的结果, 就是因为背后的行缓存。 执行printf( "Hello") 时, 因为printf是向标准输出打印的, 因此使用的是行缓存。 字符串Hello没有换行符, 所以并没有真正的I/O输出。 当执行fork时,子进程会完全复制父进程的内存空间, 因此字符串Hello也存在于子进程的行缓存中。 故而最后的输出结果中, 无论是父进程还是子进程都有Hello字符串。

    fopen函数

    1 # include<stdio.h>
    2 FILE * fopen(const char * path,const char * mode) 

    参数说明:

    (1)path:需要打开的文件路径

    (2)mode:文件打开方式,主要对应下表

    modefunction
    r 以只读方式打开文件,该文件必须存在。
    r+ 以可读写方式打开文件,该文件必须存在。
    rb+ 读写打开一个二进制文件,允许读数据。
    rt+ 读写打开一个文本文件,允许读和写。
    w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
    w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
    a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
    a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
    wb 只写打开或新建一个二进制文件;只允许写数据。
    wb+ 读写打开或建立一个二进制文件,允许读和写。
    wt+ 读写打开或着建立一个文本文件;允许读写。
    at+ 读写打开一个文本文件,允许读或在文本末追加数据
    ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。

    fdopen函数

    1 #include <stdio.h>
    2 FILE *fdopen(int fd, const char *mode);

    fdopen用于从文件描述符fd生成一个文件流FILE

    fileno函数

    1 #include <stdio.h>
    2 int fileno(FILE *stream);

    fileno则用于从文件流FILE得到对应的文件描述符

    无论是fdopen还是fileno, 关闭文件时, 都要使用fclose来关闭文件, 而不是用close。 因为只有采用此方式, fclose作为C库函数, 才会释放文件流FILE占用的内存。

    fread函数

    1 size_t fread(void *buffer, size_t size, size_t count, FILE *file);

    参数说明:

    (1)buffer是读取数据后存放地址

    (2)size是的块长度

    (3)count是块的数量,实际读取长度为size*count,返回值为块成功读取块的count数量

    fwrite函数

    1 size_t fwrite(const void *buffer, size_t size, size_t count, FILE *file);

    参数说明:

    (1)buffer是写入数据后存放地址

    (2)size是的块长度

    (3)count是块的数量,实际读取长度为size*count,返回值为块成功写入块的count数量

    下面看一个读写文件的例子:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 int main(void)
     5 {
     6     char buf[20];
     7     int ret;
     8     FILE *fp = fopen("./tmp.txt", "w+");
     9     if (!fp) {
    10     printf("Fail to open file
    ");
    11     return -1;
    12     }
    13     ret = fwrite("123", sizeof("123"), 1, fp);
    14     printf("we write %d member
    ", ret);
    15     memset(buf, 0, sizeof(buf));
    16     ret = fread(buf, 1, 1, fp);
    17     printf("We read %s, ret is %d
    ", buf, ret);
    18     fwrite("456", sizeof("456"), 1, fp);
    19     fclose(fp);
    20     return 0;
    21 }

    运行结果:

    为什么fread什么都没有读取到, 返回值是0呢?  因为每一次系统调用的readwrite成功返回后, 文件的偏移量都会被更新 。fwrite和fread操作的是同一个文件指针fp, 也就是对应的是同一个文件描述符。 第一次fwrite后, 在tmp.txt中写入了字符串“123”, 同时文件偏移为3, 也就是到了文件尾。 进行fread操作时, 既然操作的是同一个文件描述符, 自然会共享同一个文件偏移, 那么, 从文件尾自然读取不到任何数据。

    ferror函数

    1 int  ferror(FILE *stream);

    若文件流出错则返回非0,否则返回0。ferror()函数常与clearerr()函数一起使用,如果ferror(fp)发现错误,返回一个非0值,那么调用clearerr(fp)后,ferror(fp)的值变为0。

    当文件流读到文件尾时, 文件流会被设置上EOF标志。 即使不使用clearerr清除EOF标志, 有新的数据, 也可以读取成功,这是因为:文件流FILE的错误标志位只有在打开IO_DEBUG的情况下才会对后面的I/O调用产生影响: 在有错误标志位的时候, 后面的I/O调用都会直接返回EOF。 而一般情况下, IO_DEBUG这个宏是没有定义的。

    getc函数 

    1 #include <stdio.h>
    2 int fgetc(FILE *stream);
    3 int getc(FILE *stream);

    正常的情况下返回一个char,可以吗?

    不可以,这些函数返回的字符是以unsigned char形式保存,然后转换为int的。因此,在int的值域里,有效的返回值总是一个正整数。例如,遇到文件结束时,返回宏EOF。通常EOF定义成-1,在int域里,-1是没有歧义的,我们不会迷惑到底是读进来了一个字符'0xff',还是遇到了EOF。而在char域里,这样的歧义是存在的。

  • 相关阅读:
    php记录代码执行时间
    TortoiseSVN教程级别指南
    有些 where 条件会导致索引无效
    mysql优化
    SQL优化方法
    mysql服务性能优化 my.cnf my.ini配置说明详解(16G内存)
    mysql慢查询设置
    javascript和php使用ajax通信传递JSON
    PHP导出大量数据到excel表格
    核心支付业务
  • 原文地址:https://www.cnblogs.com/abc-begin/p/7672506.html
Copyright © 2011-2022 走看看