I-O流概念认知升级
在文件操作基础入门中,我们提到了流的 概念,这篇我们将更多的介绍流这个东西,以及C的I/O相关知识
现在,我们从C程序员最熟悉的printf函数开始学习I/O流。
我们对printf函数一直是很喜爱的。至少,当我们第一次向C语言的世界问好的时候,我们还是很感激它的。但是,额,但是,printf函数比起我们以为的要复杂的多,除去printf复杂的语法知识,其实,printf也没那么的安全。但是,这些都不是本文的重点,目前,我们关注的是C语言的I/O流。
printf函数会直接将其格式化参数打印到我们的屏幕,这是hello world的全部解释,但是,还不够,我们来深入了解下,我们的屏幕输出的在标准里称为标准输出stdout,而从屏幕输入的叫做标准输入stdin。这两家伙在标准库里是固定的写法,不能修改。
以下是这两者的声明(mgwin):
extern FILE _iob[]; /* A pointer to an array of FILE */
#define __iob_func() (_iob)
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
可见,stdin、stdout、stderr都是FILE* 类型的指针,他们占据了这个数组的0,1,2三个序号,所以,他们的地位和我们一般的文件流操作一样,没什么了不起的。(另外,补充一下stderr也会直接打印到屏幕)
明确了输出流的地位,我们就可以搞点事情了。
要弄明白I/O流,就要知道I流和O流都对应了哪些操作,以及二者的区别
一、I流(input流)
1.格式化I流
我们最清楚的I流操作应该就是scanf函数了,当初,我无数次的少写&符号,导致了崩溃,人也很崩溃,格式化的I流 都是使用的scanf家族函数:
int fscanf(FIlE* stream,char const *format,...);
int scanf(char const *format,...);
int sscanf(char const *string,char const *format,...);
这些函数都是从输入源读取字符并根据format字符串给出的格式化代码对它们进行转换,fscanf的输入源是作为参数给出的流,scanf从标准流stdio中读取,而sscanf则从第一个参数所给出的字符串中读取字符。
当格式化字符串到达末尾或者读取的输入不再匹配格式字符串所指定的类型时,输入就停止,在任何一种情况下,被转换的输入值的树木作为函数的返回值返回,如果在任何输入值被转换之前文件就已到达尾部,函数就返回常量值EOF。
警告:现在,解释下 为何一定要在scanf家族参数前加一个&。由于C的传值参数传递机制,把一个内存位置作为参数传递给函数的唯一方法就是传递一个指向该位置的指针。在使用如scanf时,省略&符号将导致这个变量的值作为参数传递给函数,而scanf却将其解释成指针,当将它解引用时,或者导致程序终止,或者导致一个不可预料的内存位置的数据被改写,所以一定要杜绝这种错误。
下面举例子:
while(fgets(buff,1024,stdin) != NULL)
{
if(5 != sscanf(buff,"%1d%1d%1d%1d%1d",&a,&b,&c,&d,&e))
{
fprintf(stderr,"Bad input skipped: %s",buff);
continue;
}
printf("a %d b %d c %d d %d e %d\n",a,b,c,d,e);
}
if(5!= fscanf(stdin,"%d %d %d %d %d",&a,&b,&c,&d,&e))
{
fprintf(stderr,"Bad input skipped: %s",buff);
}
printf("a %d b %d c %d d %d e %d\n",a,b,c,d,e);
总体而言,sscanf比fcanf使用面更广,需要其他例子可以参考网络。
2.非格式化I流
非格式化I流api就更多了,比如:
int getchar(void)
int getc(FILE * stream)
//gets 不使用了
int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
备注:除了getc外没有加f的都是只能操作标准stdin,其中gets函数已经禁止使用,当然,如果你要用后果自负咯。要想知道原因也请百度下。另外:fgetc和getc最大的区别在前者是函数,后者是宏,getc由fgetc通过宏实现,调用的时候注意参数stream不能是有副作用的表达式。I流为字符时,其返回值都是int类型,这一点也需要注意,因为EOF存在的缘故。
二、O流(output)
1.格式化O流
ok,下面回到printf函数家族,格式化O流油以下API:
int fprintf(FILE *stream,char const* format,...);
int printf(char const *format,...);
int sprintf(char *buffer,char const *format,...);
int snprintf(char *str, size_t size, const char *format, ...);
printf家族函数根据格式代码个format参数中的其他字符对参数列表中的值进行格式化。使用printf是将结果输出到标准输出stdout,使用fprintf是设定的任意的输出流,而sprintf把他的结果作为一个以NUL结尾的字符串存储到指定的buffer缓存区中,而不是写入到某个流中。返回值是 实际打印或者存储的字符数。
又,因为sprintf没有限定缓冲区大小的参数,所以在使用中可能会造成缓存溢出,所以如果要推荐的化,请使用snprintf函数。
关于使用方法请 看代码:
- sprintf 可以方便的将其他形式的类型转换成字符串,
char buf[100] = {0x0};
int a = 3,b =4;
sprintf(buf,"%d%d",a,b);
那么buf 就是字符串“34”;
- fprintf可以方便地将字符串写入到文件中
FILE *fp = fopen("a.txt","w");
fprintf(fp,buf);
2.非格式化O流
api如下:
int putchar(int character);
int putc(int character,FILE* stream);
int fputc(int character,FILE *stream);
int fputs(char const *buffer,FILE *stream);
int puts(char const *buffer);
备注:除了putc,没有f开头的api都只作用于stdout。
三、I/O缓存
在文章开头,我们说明了一件事,即所有的标准库的IO流操作都是基于FILE*指针类型的。即使没有明确指出,也是因为其默认为stdin或者stdout,而这两个类型也是FILE*的。
为了分析C语言的I/O缓存机制,我们来看看FILE的定义
struct _iobuf {
char *_ptr; //文件输入的下一个位置
int _cnt; //当前缓冲区的相对位置
char *_base; //指基础位置(即是文件的起始位置)
int _flag; //文件标志
int _file; //文件的有效性验证
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; //文件的大小
char *_tmpfname; //临时文件名
};
typedef struct _iobuf FILE;
绝大多数流是完全缓冲的,这意味着”读取“和”写入“实际上是从一块被称为缓冲区的内存块区域来回复制数据。从内存中来回复制数据是非常快速的,用于输出流的缓冲区只有当它写满时才会被刷新到设备或文件中。一次性把写满的缓冲区写入和逐片把程序产生的输出分别写入相比 ,效率更高。类似,输入缓存区当它为空时从通过设备或者文件读取下一块较大的输入,重新填充缓存区。
使用标准输入和输出时,这种缓冲可能会引起混淆,所以,只有当操作系统可以断定它们与交互设备并无联系时才会进行完全缓冲,否则,它们的缓存状态将因编辑器而差异,一个常见的策略是吧标准输出和标准输入联系在一起,就是当请求输入时同时刷新输出缓冲区。这样,在用户必须进行输入前,提示用户进行输入的信息和以前写入到输出缓冲区中的内容将出现在屏幕上。
改变缓冲区及其方式
在流上执行的缓冲方式有时也许并不合适,下面两个函数可以用来对缓冲区方式进行修改。这两个函数只有当指定的流被打开但还没有在它上面执行任何操作前才能被调用。
void setbuf(FILE* stream,char*buf);
int setvbuf(FILE *stream,char *buf,int mode,size_t size);
setbuf设定了一个数组来缓冲流,这个数组的字符长度必须为BUFSIZ(在stdio.h中定义好了)。如果使用NULL调用该函数,setbuf将关闭流的所有的缓冲方式,
而setvbuf更加通用,使用它,可以指定一个并非标准长度的缓冲区,也可以选择希望缓冲的缓冲方式:全缓冲、行缓冲(遇到行就刷新)或者不缓冲。
说了半天缓冲,那么刷新呢,使用函数fflush就能即刻刷新缓冲区。
四、其他知识点
- 流分为两种类型:文本(text)和二进制(binary)流,
- 使用二进制写入二进制数据 比使用字符I/O效果更高。
I-O流概念认知升级的更多相关文章
- io 流概念
io 流概念 对输入输出抽象的封装
- 认知升级x
写作目的 今天公司组织了一场关于认知升级的培训讲座,请的主管运营和发行的VP大神,收获颇多,亟待消化.这篇文章可以作为自己课后的一个笔记,自己可以反思,进而同步至团队,大家共同成长. 对于机会的认识 ...
- synchronized和 synchronized 了解偏向锁、轻量级锁、重量级锁的概念以及升级机制、以及和ReentrantLock的区别。
并发 synchronized 了解偏向锁.轻量级锁.重量级锁的概念以及升级机制.以及和ReentrantLock的区别. https://www.cnblogs.com/deltadeb ...
- JS的事件流概念*******
事件的概念 HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件.页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件. 事件流 事件流描述的 ...
- 【翻译】Flink Table Api & SQL — 流概念
本文翻译自官网:Streaming Concepts https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/st ...
- 认知升级:提升理解层次的NLP思维框架
NLP(神经语言程序学)是由理查德·班德勒和约翰·格林德在1976年创办的一门学问,美国前总统克林顿.微软领袖比尔盖茨.大导演斯皮尔博格等许多世界名人都接受过 NLP培训,世界500强企业中的 60% ...
- 李善友《认知升级之第一性原理》--507张PPT全解!_搜狐科技_搜狐网
http://www.sohu.com/a/151470602_733114
- activity 概念认知
工作流生命周期,5步 定义,工作流生命周期从流程定义开始. 发布,由开发人员打包各种资源,然后在系统管理中发布流程定义.包含流程定义文件.自定义表单.任务监听等. 执行,有具体的流程引擎如 activ ...
- 对于Mobile模块化的概念认知(小白)
最近刚刚学习了Mobile的一些基础知识,把它整理一下方便自己的学习 那什么是Mobile呢? 自己的理解是将一个项目中共同的部分抽出来,这样就形成了Mobile模块. 为什么要使用Mobile呢? ...
随机推荐
- python网络编程——SocketServer/Twisted/paramiko模块
在之前博客C/S架构的网络编程中,IO多路复用是将多个IO操作复用到1个服务端进程中进行处理,即无论有多少个客户端进行连接请求,服务端始终只有1个进程对客户端进行响应,这样的好处是节省了系统开销(se ...
- javaEE中的spring配置笔记
0 JavaEE的工程目录 0.1 WebContent 项目的主目录,在eclipse新建工程时可以自己命名,部署时会把该文件夹的内容发布到tomcat的webapps里. 该目录下可以建立 ...
- GIT使用—创建一个版本库
一.GIT命令行 [root@localhost ~]# git usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] ...
- 重置root密码后仍然不能登陆
一.忘记密码:二.输入正确用户名和密码时依旧无法登录. 一.忘记密码 进入单用户模式重置密码: 开机启动时,按‘E’键(倒计时结束前)进入界面 选择第二项,按‘E’键再次进入 在最后一行添加‘ 1’( ...
- 大牛是怎么思考设计MySQL优化方案的?
在进行MySQL的优化之前,必须要了解的就是MySQL的查询过程,很多查询优化工作实际上就是遵循一些原则,让MySQL的优化器能够按照预想的合理方式运行而已. 图-MySQL查询过程 一.优化的哲学 ...
- 如何编写自己的虚拟DOM
要构建自己的虚拟DOM,需要知道两件事.你甚至不需要深入 React 的源代码或者深入任何其他虚拟DOM实现的源代码,因为它们是如此庞大和复杂--但实际上,虚拟DOM的主要部分只需不到50行代码. 有 ...
- 20165101刘天野 2018-2019-2《网络对抗技术》Exp1 逆向与Bof基础
20165101刘天野 2018-2019-2<网络对抗技术>Exp1 逆向与Bof基础 1. 逆向及Bof基础实践说明 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执 ...
- error: 'for' loop initial declarations are only allowed in C99 mode
error: 'for' loop initial declarations are only allowed in C99 mode 出现错误: error: 'for' loop initia ...
- C++类初始化列表
转自:https://www.cnblogs.com/BlueTzar/articles/1223169.html 构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟 ...
- 六款常见的Linux操作系统推荐
家常常可能因为工作或学习的需要,要使用个操作系统(比如Windows和Linux).大家对Windwos支持的文件系统可能比较熟悉,而对Linux操作系统所支持的文件系统也许比较陌生.下面小编为大家推 ...