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呢? ...
随机推荐
- 第一课Linux系统安装知识(2)
接着上节课单击Finish按钮之后,虚拟机将会启动进入安装界面. 根据提示按回车选择图形界面安装. 这里选择Skip跳过媒介检查. 选择安装语言为简体中文,键盘鼠标默认项即可. 这里安装类型选择是定制 ...
- 学习地址 hadoop生态圈
http://my.oschina.net/leejun2005/blog/140462 http://www.codelast.com/?p=3621&cpage=1#comment-361 ...
- angularjs 的controller的三种写法
AngularJS 的controller其实就是一个方法,它有三种写法: 第一种: <pre name="code" class="javascript" ...
- [国家集训队] Crash 的文明世界(第二类斯特林数)
题目 [国家集训队] Crash 的文明世界 前置 斯特林数\(\Longrightarrow\)斯特林数及反演总结 做法 \[\begin{aligned} ans_x&=\sum\limi ...
- 3d旋转动画焦点图
在线演示 本地下载
- 20145235李涛《网络对抗》Exp8 Web基础
基础问答 什么是表单 可以收集用户的信息和反馈意见,是网站管理者与浏览者之间沟通的桥梁. 表单包括两个部分:一部分是HTML源代码用于描述表单(例如,域,标签和用户在页面上看见的按钮),另一部分是脚本 ...
- Oracle 事务处理
事务的四大特性 1.原子性(Atomicity) 事务的原子性是指事务中包含的所有操作要么都做,要么都不做,保证数据库是一致的. 2.一致性(Consistency) 一致性是指数据库在事务操作前和事 ...
- iOS开发进阶 - 日志输出框架CocoaLumberjack与XcodeColors插件的简单使用(swift版)
CocoaLumberjack是Mac和iOS上一个集快捷.简单.强大和灵活于一身的日志框架.XcodeColors是用于控制台着色的工具,配合着CocoaLumberjack用有更好的效果,不废话, ...
- java可变参数列表的实现
参数就是我们调用一个方法时需要传入得数据,在方法中可能需要参数协助处理数据或者对参数进行解析处理以达到我们希望得到的数据和结果. 平常我们在写一个方法时,我们能确定需要传入什么样的参数以及参数的个数, ...
- flume-ng源码阅读memory-channel(原创)
org.apache.flume.channel.MemoryChannel类是Flume-NG的memory-channel. private LinkedBlockingDeque<Even ...