一、流和FILE对象
  系统IO都是针对文件描述符,当打开一个文件时,即返回一个文件描述符,然后用该文件描述符来进行下面的操作,而对于标准IO库,它们的操作则是围绕流(stream)进行的。
当打开一个流时,标准IO函数fopen返回一个指向FILE对象的指针。该对象通常是一个结构,它包含了IO库为管理该流所需要的所有信息:用于实际IO的文件描述符,指向流缓存的指针,缓存的长度,当前在缓存中的字符数,出错标志等等。
我们称指向FILE对象的指针(类型为FILE *)为文件指针。
二、缓存
  标准IO提供缓存的目的是尽可能减少使用read和write调用的数量。标准IO提供了三种类型的缓存
    (1) 全缓存。在这种情况下,当填满标准IO缓存后才进行实际IO操作。对于驻在磁盘上的文件通常是由标准IO实施全缓存的。在一个流上执行第一次IO操作时,相关标准IO函数通常调用malloc获得所需的缓存。
    术语刷新(flush)说明标准IO缓存的写操作。缓存可由标准IO例程自动地刷新(例如当填满一个缓存时),或者可以调用函数flush刷新一个流。在UNIX环境中刷新有两种意思。在标准IO库方面,刷新意味着将缓存中的内容写入到磁盘上(该缓存可以只是局部填写     的)。在终端驱动程序方面,刷新表示丢弃已存在缓存中的数据。
    (2) 行缓存。在这种情况下,当在输入和输出中遇到换行符时,标准IO库执行IO操作。这允许我们一次输入一个字符(用标准IO fputc函数),但只有在写了一行之后才进行实际IO操作。当流涉及一个终端时(例如标准输入和标准输出),典型的使用行缓存。
    对于行缓存有两个限制,一是:因为标准IO库用来收集每一行的缓存长度是固定的,所以只要填满了缓存,那么即使还没有写一个换行符,也进行IO操作。第二是:任何时候只要通过标准输入输出库要求从(a)一个不带缓存的流(b)一个行缓存的流(它预先要求从内    核得到数据)得到输入数据,那么就会造成刷新所有行缓存输出流。在(b)中带了一个在扩号中的说明的理由是,所需的数据可能已在缓存中,它并不要求内核在需要该数据时才进行操作。很明显,从不带缓存的一个流中进行输入((a)项)要求当时从内核中得到数    据。
    (3) 不带缓存。标准IO库不对字符进行缓存。标准出错流通常不带缓存,这可以使出错信息尽快的显示出来。

  ASSI C要求以下缓存特征:
    (1) 当且仅当标准输入和标准输出不涉及交互作用设备时,他们才是全缓存的。
    (2) 标准出错绝不是全缓存的。
  对于任何一个流如果我们不喜欢系统默认则可以通过以下两个函数来更改缓存类型:

#include <stdio.h>
void setbuf(FILE *fp, char *buf);
int setvbuf(FILE *fp, char *buf, int mode, size_t size);

  这些函数必须在流打开后和对该流进行任何操作之前调用。
  可以使用setbuf打开或关闭缓存机制。为了带缓存进行IO,参数buf必须指向一个长度为BUFSIZ的缓存(该常数定义在stdio.h中)。通常在此之后该流就是全缓存的,但是该流与终端设备相关,那么某些系统也可将其设置为行缓存。为了关闭缓存,将buf设置为NULL。
  使用setvbuf可以精确的说明所需的缓存类型。由mode参数指定:

_IOFBF 全缓存
_IOLBF 行缓存,
_IONBF 不带缓存

  如果指定了一个不带缓存的流,则忽略buf和size参数。如果指定全缓存或行缓存,则buf和size可以选择地指定一个缓存及长度。如果该流是带缓存的,而buf是NULL,则标准IO库将自动的为该流分配适当长度的缓存,适当长度是指struct stat结构体中的st_blksize所指定的值。如果系统不能为为该流决定此值(例如该流涉及一个设备或一个管道),则分配长度为BUFSIZ的缓存。
  下表列出了这两个函数的动作,以及它们的各个选择项

  

  如果在一个函数中分配了一个自动变量类的标准IO缓存,则从该函数返回之前必须关闭该流。SVR4将缓存的一部分用于ta自己的管理操作,所以可以存放在缓存中的实际数据字节数小于size。一般来说,应由系统选择缓存的长度,并自动分配缓存。在这样处理时,标准IO库在关闭此流时将自动关闭释放缓存。
  可以在任何时候强制刷新一个流:

#include <stdio.h>
int fflush(FILE *fp);

  此函数使该流所有的数据传递到内核,如果fp是NULL,则此函数刷新所有输出流。

三、打开流

#include <stdio.h>
FILE *fopen(const char *pathname, const char *type);
FILE *freopen(const char *pathname, const char *type, FILE fp);
FILE *fdopen(int fileds, const char *type);
返回值: 成功文件指针,失败NULL

  三个函数的区别:

    (1)fopen打开路径为pathname的文件。
    (2)freopen在一个特定流上(由fp指定)打开pathname文件。如果该流已打开则先关闭。此函数一般用于将一个文件打开为一个预定义的流:标准输入、标准输出和标准出错。
    (3)fdopen取一个现存的文件描述符(可能从open\dup\dup2\fcntl\pipe函数得到此文件描述符)并使一个标准的IO流与该文件描述符结合。此函数常用于由创建管道和网络通信通道函数获得的插入符。因为这些特殊类型的文件不能用标准IO fopen打开
  type参数指定对该IO流的读、写方式,ANSI C规定type参数可以有15种值:

  

  使用字符b作为type的一部分使得标准IO系统可以区分文本文件或二进制文件。因为UNXI并不对这两种文件区分所以在UNIX环境下指定b作为type的一部分实际上并无作用。
  对于fdopen,type参数的意义有些区别。因为文件描述符已打开,所以fdopen为写而打开并不截短该文件。另外,标准IO添加方式也不能用于创建文件,因为如果一个文件描述符引用一个文件则该文件一定已经存在。
  当用添加类型打开一个文件后则每次写都将数据写到文件当前尾端处。如果有多个进程用标准IO的方式打开同一个文件,则来自每个进程的数据都将正确的写到文件中。

  当以读和写打开一文件时(type中+号),具有如下限制:
    如果中间没有fflush、fseek、fsetpos或rewind,则在输出的后面不能直接跟随输入。
    如果中间没有fseek、fsetpos、或rewind或者一个输出操作没有到达文件尾端,则在输入操作之后不能直接跟随输出。
  下表是打开一个流的六种不同的方式:
  


  在指定w或a类型创建一个新文件时,无法说明文件的存取许可位,POSIX.1要求以这种方式创建的文件具有以下权限:

S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH

  除非流引用终端设备,否则按系统默认,它被打开时是全缓存。若流引用终端设备则该流是行缓存。
  用fclose关闭一个打开的流:

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

  在该文件关闭之前,刷新缓存中的输出数据。缓存中的输入数据被丢弃。如果标准IO库已经为该流自动分配了一个缓存则释放该缓存。
  当一个进程正常终止时(直接调用exit()函数或者从main函数中返回),则所有带未写缓存数据的标准IO流都被刷新,所有打开的标准IO流都被关闭。

四、读和写流
  一旦打开了流,则可以在以下三种不同类型的非格式化IO中进行选择,对其进行读写:
    (1) 每次一个字符的IO。一次读或写一个字符,如果流是带缓存的,则标准IO函数处理所有缓存。
    (2) 每次一行的IO。使用fgets和fputs一次读或写一行。每行都以一个新行符结束。当调用fgets时应说明能处理的最大行长。
    (3) 直接IO。 fwrite和fread函数支持这种类型的IO。每次IO操作读或写某种数量的对象,每个对象具有指定的长度。这两个函数常用于从二进制文件中读或写一个结构。

  直接IO(direct IO)这个术语来自ANSI C标准,有时也被称为:二进制IO、一次一个对象IO、面向记录的IO或面向结构的IO。

  1.输入函数
  以下三个函数用于一次读一个字符:

#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
返回值:成功则为下一个字符,如果已处于文件尾端或出错则为EOF

  getchar等同于getc(stdin)。前两个函数的区别是getc可被实现为宏,而fgetc则不能。(这里的可被实现为宏即大多数UNIX系统中getc的实现是这样:在<stdio.h>中#define getc(FILE *fp) xxx(FILE *fp),即getc不是一个函数而是一个宏)这意味着:
    (1) getc的参数不应当是具有副作用的表达式。
    (2) 因为fgetc一定是个函数,所以可得到其地址。这就允许将fgetc的地址作为一个参数传送给另一个函数。
    (3) 调用fgetc所需时间可能长于调用getc,因为调用函数通常所需的时间长于调用宏。

  这三个函数以unsigned char类型转换为int的方式返回下一个字符。返回int的原因是函数可以返回一个负值(指示出错或已到达文件尾端),在<studio.h>中常数EOF常要求是一个负值,其值经常是-1。所以不能将这三个函数的返回值放到一个字符变量中。

  不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,须调用ferror或feof。

#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
返回值:条件为真返回非0值,否则0
void clearerr(FILE *fp);

  在大多数实现的FILE对象中,为每个流保持了两个标志

  • 出错标志
  • 文件结束标志

  

  从一个流读之后,可以调用ungetc将字符再送回到流中。

#include <stdio.h>
int ungetc(int c, FILE *fp);

  送回到流中的字符又可以从流中读出,但读出字符的顺序与送回的顺序相反。回送的字符不一定是上一次读到的字符。EOF不能回送。但是当已到达文件尾端时仍可以回送一个字符。下次读将返回该字符,再次读则返回EOF。之所以可以这样做的原因是一次成功的ungetc调用会清除该流的文件结束指示。

  当正在读一个输入流,并进行某种形式的分字或分记号操作时,会经常用到回送字符操作。有时需要先看一看下一个字符以决定如何处理当前字符。然后就需要方便的将刚查看的字符送回,以便下一次调用getc时返回该字符。

  2. 输出函数

  

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

  putchar等同于putc(c, stdout)

五、 每次一行IO

  下面两个函数提供每次输入一行的功能:

#include <stdio.h>
char *fgets(char *buf, int n, FILE *fp);
char *fgets(char *buf);

  gets从标准输入读。对于fgets必须指定缓存buf的长度n。此函数会一直读到下一个新行符为止,但是不超过n-1个字符,读入的字符被送入到缓存。该缓存以null字符结尾。如果该行包括最后一个新行符的字符数超过n-1,则只返回一个不完整的行,而且缓存总是以null字符结尾。对fgets的下一次调用会继续该行。

  gets函数不被推荐使用因为不能指定缓存的长度,gets并不将新行符存入缓存中。

#include <stdio.h>
int fputs(const char *str, FILE *fp);
int puts(const char *str);
返回值:成功返回非负值,出错EOF

  函数fputs将一个以null符终止的字符串写入到指定的流,终止符null不写出。这并不一定是每次输出一行,因为它并不要求在null符之前一定是新行符,但是通常在null符之前是一个新行符。

  puts将一个以null符终止的字符串写入到标准输出,终止符不写出。但是puts然后将一个新行符写到标准输出

[APUE]标准IO库(上)的更多相关文章

  1. [APUE]标准IO库(下)

    一.标准IO的效率 对比以下四个程序的用户CPU.系统CPU与时钟时间对比 程序1:系统IO 程序2:标准IO getc版本 程序3:标准IO fgets版本 结果: [注:该表截取自APUE,上表中 ...

  2. C5 标准IO库:APUE 笔记

    C5 :标准IO库 在第三章中,所有IO函数都是围绕文件描述符展开,文件描述符用于后续IO操作.由于文件描述符相关的操作是不带缓冲的IO,需要操作者本人指定缓冲区分配.IO长度等,对设备环境要求一定的 ...

  3. c++ primer 学习杂记3【标准IO库】

    第8章 标准IO库 发现书中一个错误,中文版p248 流状态的查询和控制,举了一个代码例子: int ival; // read cin and test only for EOF; loop is ...

  4. 18、标准IO库详解及实例

    标准IO库是由Dennis Ritchie于1975年左右编写的,它是Mike Lestbain写的可移植IO库的主要修改版本,2010年以后, 标准IO库几乎没有进行什么修改.标准IO库处理了很多细 ...

  5. 文件IO函数和标准IO库的区别

    摘自 http://blog.chinaunix.net/uid-26565142-id-3051729.html 1,文件IO函数,在Unix中,有如下5个:open,read,write,lsee ...

  6. C++ Primer 读书笔记: 第8章 标准IO库

    第8章 标准IO库 8.1 面向对象的标准库 1. IO类型在三个独立的头文件中定义:iostream定义读写控制窗口的类型,fstream定义读写已命名文件的类型,而sstream所定义的类型则用于 ...

  7. 高级UNIX环境编程5 标准IO库

    标准IO库都围绕流进进行的 <stdio.h><wchar.h> memccpy 一般用汇编写的 ftell/fseek/ftello/fseeko/fgetpos/fsetp ...

  8. 第十三篇:带缓冲的IO( 标准IO库 )

    前言 在之前,学习了 read write 这样的不带缓冲IO函数. 而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 wri ...

  9. 带缓冲的IO( 标准IO库 )

    前言 在之前,学习了 read write 这样的不带缓冲IO函数.而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 writ ...

随机推荐

  1. 菜鸟学Struts2——Struts工作原理

    在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...

  2. rnandroid环境搭建

    react-native 环境搭建具体步骤这个大家已经玩烂了,这个主要是记录下来自己做win7系统遇到的坑 1.com.android.ddmlib.installexception 遇到这个问题,在 ...

  3. shell变量

    定义变量 定义变量时,变量名不加美元符号($),如: variableName="value" 注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样.同时,变量名 ...

  4. Java 8 的 Nashorn 脚本引擎教程

    本文为了解所有关于 Nashorn JavaScript 引擎易于理解的代码例子. Nashorn JavaScript 引擎是Java SE 8的一部分,它与其它像Google V8 (它是Goog ...

  5. 简记某WebGIS项目的优化之路

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 该项目为研究生时的老师牵头,个人已毕业数年,应老师要求协助其 ...

  6. Java消息队列--ActiveMq 实战

    1.下载安装ActiveMQ ActiveMQ官网下载地址:http://activemq.apache.org/download.html ActiveMQ 提供了Windows 和Linux.Un ...

  7. WPF中Grid实现网格,表格样式通用类

    /// <summary> /// 给Grid添加边框线 /// </summary> /// <param name="grid"></ ...

  8. 分享两个BPM配置小技巧

    1.小技巧 流程图修改后发布的话版本号会+1,修改次数多了之后可能会导致版本号很高,这个时候可以将流程导出,然后删除对应的流程包再导入,发布数据模型和流程图之后,版本清零 2.小技巧 有的同事入职后使 ...

  9. 在树莓派Raspbian下安装支持Hard Float的.NET环境

    [题外话] 最近入了个树莓派玩,系统装的官方推荐的Hard Float的Raspbian,由于衍生自Debian,所以Mono什么的非常好装.但是官方源中的Mono在Hard Float的Raspbi ...

  10. mono for android学习过程系列教程(3)

    服务 接着上一讲的内容,咱们继续来唠叨概念性的东西.服务,内容提供器,广播接收器等理论知识. 首先是服务,它不是一个可视化的组件或者视图.他是由我们开发人员来定义,可以一直一直运行 的工作单元.跟活动 ...