[APUE]标准IO库(上)
一、流和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库(上)的更多相关文章
- [APUE]标准IO库(下)
一.标准IO的效率 对比以下四个程序的用户CPU.系统CPU与时钟时间对比 程序1:系统IO 程序2:标准IO getc版本 程序3:标准IO fgets版本 结果: [注:该表截取自APUE,上表中 ...
- C5 标准IO库:APUE 笔记
C5 :标准IO库 在第三章中,所有IO函数都是围绕文件描述符展开,文件描述符用于后续IO操作.由于文件描述符相关的操作是不带缓冲的IO,需要操作者本人指定缓冲区分配.IO长度等,对设备环境要求一定的 ...
- c++ primer 学习杂记3【标准IO库】
第8章 标准IO库 发现书中一个错误,中文版p248 流状态的查询和控制,举了一个代码例子: int ival; // read cin and test only for EOF; loop is ...
- 18、标准IO库详解及实例
标准IO库是由Dennis Ritchie于1975年左右编写的,它是Mike Lestbain写的可移植IO库的主要修改版本,2010年以后, 标准IO库几乎没有进行什么修改.标准IO库处理了很多细 ...
- 文件IO函数和标准IO库的区别
摘自 http://blog.chinaunix.net/uid-26565142-id-3051729.html 1,文件IO函数,在Unix中,有如下5个:open,read,write,lsee ...
- C++ Primer 读书笔记: 第8章 标准IO库
第8章 标准IO库 8.1 面向对象的标准库 1. IO类型在三个独立的头文件中定义:iostream定义读写控制窗口的类型,fstream定义读写已命名文件的类型,而sstream所定义的类型则用于 ...
- 高级UNIX环境编程5 标准IO库
标准IO库都围绕流进进行的 <stdio.h><wchar.h> memccpy 一般用汇编写的 ftell/fseek/ftello/fseeko/fgetpos/fsetp ...
- 第十三篇:带缓冲的IO( 标准IO库 )
前言 在之前,学习了 read write 这样的不带缓冲IO函数. 而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 wri ...
- 带缓冲的IO( 标准IO库 )
前言 在之前,学习了 read write 这样的不带缓冲IO函数.而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 writ ...
随机推荐
- Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求
上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...
- ABP文档 - 目录
ABP框架 概览 介绍 多层结构 模块系统 启动配置 多租户 集成OWIN 共同结构 依赖注入 会话 缓存 日志 设置管理 时间 领域层 实体 值对象(新) 仓储 领域服务 工作单元 领域事件(Eve ...
- (系统架构)标准Web系统的架构分层
标准Web系统的架构分层 1.架构体系分层图 在上图中我们描述了Web系统架构中的组成部分.并且给出了每一层常用的技术组件/服务实现.需要注意以下几点: 系统架构是灵活的,根据需求的不同,不一定每一层 ...
- 千呼万唤始出来,微软Power BI简体中文版官网终于上线了,中文文档也全了。。
前几个月时间,研究微软Power BI技术,由于没有任何文档和资料,只能在英文官网瞎折腾,同时也发布了英文文档的相关文章:系列文章,刚好上周把文章发布完,结果简体中文版上线了.哈哈,心里有苦啊,早知道 ...
- jQuery学习之路(4)- 动画
▓▓▓▓▓▓ 大致介绍 通过jQuery中基本的动画方法,能够轻松地为网页添加非常精彩的视觉效果,给用户一种全新的体验 ▓▓▓▓▓▓ jQuery中的动画 ▓▓▓▓▓▓ show()和hide()方法 ...
- UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中
前一段开发UWP应用的时候因为系统返回按钮事件(SystemNavigationManager.GetForCurrentView().BackRequested)浪费了不少时间.现象就是在手机版的详 ...
- IE8/9 JQuery.Ajax 上传文件无效
IE8/9 JQuery.Ajax 上传文件有两个限制: 使用 JQuery.Ajax 无法上传文件(因为无法使用 FormData,FormData 是 HTML5 的一个特性,IE8/9 不支持) ...
- 谈一谈NOSQL的应用,Redis/Mongo
1.心路历程 上年11月份来公司了,和另外一个同事一起,做了公司一个移动项目的微信公众号,然后为了推广微信公众号,策划那边需要我们做一些活动,包括抽奖,投票.最开始是没有用过redis的,公司因为考虑 ...
- [转载]强制不使用“兼容性视图”的HTML代码
在IE8浏览器以后版本,都有一个"兼容性视图",让不少新技术无法使用.那么如何禁止浏览器自动选择"兼容性视图",强制IE以最高级别的可用模式显示内容呢?下面就介 ...
- 利用poi导出Excel
import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.r ...