《Unix环境高级编程》读书笔记 第5章-标准I/O流
1. 引言
- 标准I/O库由ISO C标准说明,由各个操作系统实现
- 标准I/O库处理很多细节,如缓冲区分配、以优化的块长度执行I/O等。这些处理使用户不必担心如何使用正确的块长度,这使得它便于用于使用,但是如果不深入地了解I/O库函数的操作,也会带来一些问题。
2. 流和FILE对象
- 第3章中,所有I/O函数都是围绕文件描述符的;对于标准I/O库,它们的操作是围绕流
FILE *
进行的,称其为文件指针
。 FILE对象通常是一个结构,它包括了标准I/O库为管理该流需要的所有信息,包括用于实际I/O的文件描述符、指向用于该流缓冲区的指针、缓冲区的长度、当前在缓冲区中的字符数以及出错标志等。
标准I/O文件流可用于单字节或多字节(“宽”)字符集。
- 流的定向决定了所读、写的字符是单字节还是多字节的。当一个流最初被创建时,它并没有定向。若在为定向的流上使用一个多字节I/O函数,则将流的定向设置为
宽定向
的;若在为定向的流上使用一个单字节I/O函数,则将流的定向设置为字节定向
的。
#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
Returns: positive if stream is wide oriented,
negative if stream is byte oriented,
or 0 if stream has no orientation
- 根据mode参数的不同字,fwide函数执行不同的工作。
- mode为负,则字节定向;
- mode为正,则宽定向;
- mode为0,则不设置流的定向,fwide返回标识该流定向的值
- 注意,fwide并不改变已定向流的定向
3. 标准输入、标准输出、标准错误
- 3个标准I/O流通过预定义文件指针(即FILE *) stdin、stdout、stderr加以引用。
4. 缓冲
标准I/O库提供缓冲的目的是尽可能减少使用read和write调用的次数。它对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。遗憾的是,标准I/O库最令人迷惑的也是它的缓冲。
标准I/O提供了以下3种类型的缓冲:
- 全缓冲。在填满标准I/O缓冲区后才进行实际I/O操作。对于驻留在磁盘上的文件通常由标准I/O库实施全缓冲。缓冲区可由标准I/O例程自动地冲洗(flush),也可通过调用函数fflush冲洗一个流。
- 术语flush有两种意思:在标准I/O库方面,flush(冲洗)意味着缓冲区中的内容写到磁盘上;在终端驱动程序方面,flush(刷清)表示丢弃已存储在缓冲区中的数据。
- 行缓冲。当输入或输出中遇到换行符时,标准I/O库执行I/O操作。当流涉及一个终端时,通常使用行缓冲。
- 对于行缓冲有两个限制:
- 只要填满了缓冲区,即使还没遇到换行符,也进行I/O操作;
- 任何时候只要通过标准I/O库要求从(a)
一个
不带缓冲的流,或者(b)一个
行缓冲的流得到输入数据,那么就会flush所有
行缓冲输出流。
- 对于行缓冲有两个限制:
- 不带缓冲。标准I/O库不对字符进行缓冲存储。如fputs函数。标准错误流stderr通常是不带缓冲的。
- 全缓冲。在填满标准I/O缓冲区后才进行实际I/O操作。对于驻留在磁盘上的文件通常由标准I/O库实施全缓冲。缓冲区可由标准I/O例程自动地冲洗(flush),也可通过调用函数fflush冲洗一个流。
ISO C要求下列缓冲特征:
- 当且仅当标准输入和标准输出并不指向交互式设备时,它们才是全缓冲的
- 标准错误决不会是全缓冲的
很多系统默认使用下列类型的缓冲:
- 标准错误是不带缓冲的
- 若是指向终端设备的流,则是行缓冲的;否则是全缓冲的
#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf );
// 使用该函数打开或关闭缓冲机制。参数buf必须指向一个长度为BUFSIZ的缓冲区;或为NULL以关闭缓冲
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
Returns: 0 if OK, nonzero on error
- 上面两个函数必须在流被打开后,且对流执行任何一个其他操作之前调用
- mode参数:_IOFBF全缓冲、_IOLBF行缓冲、_IONBF不带缓冲
- 如果指定全缓冲或行缓冲,则buf和size可选择地指定一个缓冲区及其长度。若流带缓冲而buf是NULL,则标准I/O库将自动地为该流分配适当长度的缓冲区。
#include <stdio.h>
int fflush(FILE *fp); // 若fp为NULL,则导致所有输出流被冲洗
Returns: 0 if OK, EOF on error
5. 打开流
- 下列3个函数打开一个标准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 fd, const char *type); // 取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合
All three return: file pointer if OK, NULL on error
- 打开标准I/O流的type参数
- 字符b,代表二进制。但Unix内核并不对文本文件和二进制文件进行区分
- 注意:在指定w或a类型创建一个新文件时,我们无法说明该文件的访问权限位;而open和creat可以
如果以读和写类型打开一个文件时(type中带+号),具有下列限制:
- 如果中间没有fflush、fseek、fsetpos或rewind,则在输出的后面不能直接跟随输入
- 如果中间没有fseek、fsetpos或rewind,或者一个输入操作没有到达文件尾端,则在输入操作之后不能直接跟随输出
打开一个标准I/O流的6种不同方式
- 除非流引用终端设备,否则按系统默认,流被打开是全缓冲的。若流引用终端设备,则该流是行缓冲的。
#include <stdio.h>
int fclose(FILE *fp);
Returns: 0 if OK, EOF on error
- 当一个进程正常终止时(调用exit或从main函数返回),则所有带未写缓冲数据的标准I/O流都被冲洗,所有打开的标准I/O流都被关闭。
6. 读和写流
一旦打开了流,可在
3种
不同类型的非格式化I/O中进行选择,对其进行读、写操作- 每次一个字符的I/O
- 每次一行的I/O
- 直接I/O(二进制I/O、面向记录的I/O、一次一个对象的I/O)
在大多数实现中,为每个流在FILE对象中维护了两个标志:
- 出错标志
- 文件结束标志
区分是出错还是到达文件尾端,因为这两种情况下返回值相同
#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
Both return: nonzero (true) if condition is true, 0 (false) otherwise
void clearerr(FILE *fp); // 调用clearerr可以清除这两个标志
7. 每次一个字符的I/O
- 输入函数
#include <stdio.h>
int getc(FILE *fp); // 可被实现为宏,故参数不应当是具有副作用的表达式;返回值是int,因为常量EOF是-1
int fgetc(FILE *fp); // 一定是个函数
int getchar(void); // getc(stdin)
All three return: next character if OK, EOF on end of file or error
#include <stdio.h>
int ungetc(int c, FILE *fp); // 将字符再压送回流中,可回送多次,但再次读出字符的顺序与压送回的顺序相反。一次一字符
// 作用:有时需要查看下一个字符,以决定对当前字符如何处理
Returns: c if OK, EOF on error
- 输出函数
#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
All three return: c if OK, EOF on error
8. 每次一行I/O
#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp); // 存入换行符
char *gets(char *buf ); // 不推荐使用,可能造成缓冲区溢出;并不将换行符存入缓冲区中
Both return: buf if OK, NULL on end of file or error
#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp); // 不一定换行
int puts(const char *str); // 必定输出一换行符
Both return: non-negative value if OK, EOF on error
9. 标准I/O的效率
10. 二进制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);
Both return: number of objects read or written
- 两种常见的用法:
- 读或写一个二进制数组
- 读或写一个结构
- fread和fwrite函数返回读或写的对象数。fread可能少于nobj;fwrite若少于nobj则出错。
- 使用二进制I/O的基本问题是:它只能用于读在同一个系统上已写的数据。其原因是:
- 在一个结构中,同一成语的偏移量可能随编译程序和系统的不同而不同。
- 用来存储多字节整数和浮点数的二进制格式在不同的系统结构间也可能不同。
- 在不同系统之间交换二进制数据的实际解决方法是:使用互认的规范格式。
11. 定位流
- 有3种方法定位标准I/O流
- ftell和fseek函数。它们假定文件的位置可以存放在一个长整型中
- ftello和fseeko函数。使用
off_t
数据类型代替long - fgetpos和fsetpos函数。由ISO C引入的。使用一个抽象数据类型
fpos_t
记录文件的位置。需要移植到非Unix系统上运行的应用程序使用fgetpos和fsetpos
#include <stdio.h>
long ftell(FILE *fp);
Returns: current file position indicator if OK, −1L on error
int fseek(FILE *fp, long offset, int whence);
Returns: 0 if OK, −1 on error
void rewind(FILE *fp);
#include <stdio.h>
off_t ftello(FILE *fp);
Returns: current file position indicator if OK, (off_t)−1 on error
int fseeko(FILE *fp, off_t offset, int whence);
Returns: 0 if OK, −1 on error
#include <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);
Both return: 0 if OK, nonzero on error
12. 格式化I/O
- 格式化输出
#include <stdio.h>
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...); // 写到指定的流
int dprintf(int fd, const char *restrict format, ...); // 写到指定的文件描述符
All three return: number of characters output if OK, negative value if output error
int sprintf(char *restrict buf, const char *restrict format, ...);
// 写到数组buf,末尾加null字节,但该字节不包括在返回值中;可能溢出,故不推荐使用它
Returns: number of characters stored in array if OK, negative value if encoding error
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...); // 显式指定了缓冲区长度n
Returns: number of characters that would have been stored in array
- 使用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 vdprintf(int fd, const char *restrict format, va_list arg);
All three return: number of characters output if OK, negative value if output error
int vsprintf(char *restrict buf, const char *restrict format, va_list arg);
Returns: number of characters stored in array if OK, negative value if encoding error
int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg);
Returns: number of characters that would have been stored in array
- 格式化输入
#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, ...);
All three return: number of input items assigned,
EOF if input error or end of file before any conversion
- 调用fileno函数以获得其描述符
#include <stdio.h>
int fileno(FILE *fp);
Returns: the file descriptor associated with the stream
13. 临时文件
- 创建临时文件
#include <stdio.h>
char *tmpnam(char *ptr);
Returns: pointer to unique pathname
FILE *tmpfile(void);
Returns: file pointer if OK, NULL on error
- tmpnam函数产生一个与现有文件名不同的一个有效路径名字符串。最多调用次数TMP_MAX(stdio.h中定义)
- 若ptr为NULL,则所产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回。后续调用tmpnam函数会重写该静态区
- 若ptr不为NULL,则认为它应该是指向长度至少是L_tmpnam(stdio.h中定义)个字符的数组
- tmpfile函数创建一个临时二进制文件(类型wb+),在关闭该文件或程序结束时间自动删除这种文件
#include <stdlib.h>
char *mkdtemp(char *template);
Returns: pointer to directory name if OK, NULL on error
int mkstemp(char *template);
Returns: file descriptor if OK, −1 on error
- 名字是通过template字符串进行选择的。这个字符串的后6位设置为
XXXXXX
的路径名。 - mkdtemp创建一个目录,返回新目录的名字,其访问权限位集:S_IRUSR | S_IWUSR | S_IXUSR
- mkstemp创建一个普通文件并以读写方式打开该文件,访问其文件描述符,其访问权限位集:S_IRUSR | S_IWUSR
- mkstemp函数与tmpfile不同,其创建的临时文件不会自动删除
- 使用tmpnam和tempnam(未列出其原型)至少有一个缺点:在返回唯一的路径名和使用该路径名创建文件之间存在一个时间窗口;而tmpfile和mkstemp函数则不会
14. 内存流
- 在SUSv4中支持内存流,内存流是没有底层文件支持,但仍使用FILE指针进行访问的标准I/O流
- 有3个函数可用于内存流的创建,第1个是fmemopen函数,它允许调用者提供缓冲区用于内存流
#include <stdio.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);
Returns: stream pointer if OK, NULL on error
- fmemopen函数允许调用者提供缓冲区用于内存流,buf参数指向缓冲区的开始位置,size为缓冲区的字节数
- 当buf为NULL时,函数自动分配size字节的缓冲区,这种情况下,当流关闭时缓冲区会被释放
- type参数控制如何使用流
- type参数的这些取值与基于文件的标准I/O流的type参数有些微小差别,具体参见书中讲解
#include <stdio.h>
FILE *open_memstream(char **bufp, size_t *sizep); // 创建的流是面向字节的
#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep); // 创建的流是面向宽字节的
Both return: stream pointer if OK, NULL on error
- 这两个函数与fmemopen函数的不同在于:
- 创建的流只能写文件
- 不能指定自己的缓冲区,当可以分别通过bufp和sizep参数访问缓冲区地址和大小
- 关闭流后需要自行释放缓冲区
- 对流添加字节会增加缓冲区大小
15. 标准I/O的替代软件
- 快速I/O库 fio
- sfio
- mmap函数
- uClibc C库
- Newlib C库
原创文章,转载请注明出处: http://www.cnblogs.com/DayByDay/p/3893905.html
转载于:https://www.cnblogs.com/DayByDay/p/3893905.html
《Unix环境高级编程》读书笔记 第5章-标准I/O流相关推荐
- 《UNIX环境高级编程》笔记 第五章-标准IO库
1. 流和FILE对象 在第三章的系统调用都是围绕文件描述符fd的.但是标准I/O库函数操作则是围绕流进行的.当使用标准I/O库打开或创建一个文件时,使用一个流与一个文件关联. 当打开一个流时,标准I ...
- 文件和目录(二)--unix环境高级编程读书笔记
在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...
- 高级IO(一)--UNIX环境高级编程读书笔记
在前面学习了文件IO,标准IO和终端IO,现在学习高级IO,UNIX中怎么有这么多的IO. 1.非阻塞IO 可以将系统调用分为两类:低速系统调用和其他.低速系统调用是可能会使进程永远阻塞的一类系统调用 ...
- unix进程的环境--unix环境高级编程读书笔记
1.进程的启动 进程总是从 main 函数开始执行的,main函数的函数原型如下: int main(int argc,char* argv[]); 当内核启动 c 程序时,使用一个 ...
- 文件io(一)--unix环境高级编程读书笔记
unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...
- linux信号(二)--unix环境高级编程读书笔记
1.信号集 在linux中,可以用一个称为信号集的数据类型 sigset_t,来表示所有的被阻塞信号的一个集合.对这个集合的操作函数有: #include <signal.h>int s ...
- linux进程控制(一)--unix环境高级编程读书笔记
1.进程PID和特殊的3个进程 每一个进程在系统中都有一个唯一的标识,这个标识叫做进程标识符,或者叫 PID(process identity).我们可以通过调用 getpid 函数来获取一个进 ...
- APUE Unix环境高级编程读书笔记
.. 转载于:https://www.cnblogs.com/solitrarychen/p/5407536.html
- linux系统数据文件和信息--unix环境高级编程读书笔记
linux系统中的数据文件有很多,在这一章里介绍的主要内容是和系统有关的一系列文件,包括passwd,shadow,group,utmp,wtmp以及一些系统的相关信息和时间的相关操作. 1.pass ...
- 标准IO库--unix环境高级编程读书笔记
标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...
最新文章
- hdu 4309(最大流+枚举状态)
- 一种简单快捷的 java 热部署方式
- SVT-AV1:开源编解码最新进展
- 【NOIP模拟】彩色树【树形dp】【树链剖分性质】【复杂度分析】
- c语言程序图片马赛克,关于c语言的图像均值滤波 请问大神为什么我的结果都是马赛克...
- python模型的属性是什么_python – Django:为什么Django模型字段的类属性?
- 第三方框架-纯代码布局:Masonry的简单使用
- java 单行文本_Java Swing界面编程(17)---单行文本输入组件:JTextField
- C++#ifndef/#define/#endif的用法
- 干货:大米云LAMP使用说明
- 我儿子今年15周岁,学习不好,去年上的高职,今年我犹豫是让他继续上,还是学个手艺?...
- 0基础学习音视频路线,以及重磅音视频资料下载
- 心形函数的正确打开方式(Unity3D Shader)
- SEC主席Gary Gensler在被问及以太坊是否是证券时,选择了沉默
- react里面 内联css样式怎么样_简单的使用Radium管理React中的内联样式
- help efun matlab,Matlab优化工具箱在函数最值求解中的应用.pdf
- springboot中使用Mybatis_plus
- SQL Sever 远程计算机拒绝网络连接,错误:1225 具体解决步骤。
- 【成功解决】warning: #1035-D: single-precision operand implicitly conve
- 游戏名称:猜人名游戏
热门文章
- http://longshuai2007.blog.163.com/blog/static/1420
- 三层交换的测试1:级联的傻HUB
- Oracle脚本(二)
- 艾伟也谈项目管理,开始一个项目时最重要的是什么?
- Android学习小Demo(9)一个To Do List的实现
- 做好准备,让你的短信应用迎接Android 4.4(KitKat)
- [iCustomer] 项目技术简介
- 05mycat父子表
- 企业微信jssdk分享接口管理系统
- Exchange Server 2016管理系列课件22.通讯组概述