文件操作基础入门中,我们提到了流的 概念,这篇我们将更多的介绍流这个东西,以及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流概念认知升级的更多相关文章

  1. io 流概念

    io 流概念 对输入输出抽象的封装

  2. 认知升级x

    写作目的 今天公司组织了一场关于认知升级的培训讲座,请的主管运营和发行的VP大神,收获颇多,亟待消化.这篇文章可以作为自己课后的一个笔记,自己可以反思,进而同步至团队,大家共同成长. 对于机会的认识 ...

  3. synchronized和 synchronized 了解偏向锁、轻量级锁、重量级锁的概念以及升级机制、以及和ReentrantLock的区别。

    并发 synchronized 了解偏向锁.轻量级锁.重量级锁的概念以及升级机制.以及和ReentrantLock的区别.       https://www.cnblogs.com/deltadeb ...

  4. JS的事件流概念*******

    事件的概念 HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件.页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件. 事件流 事件流描述的 ...

  5. 【翻译】Flink Table Api & SQL — 流概念

    本文翻译自官网:Streaming Concepts  https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/st ...

  6. 认知升级:提升理解层次的NLP思维框架

    NLP(神经语言程序学)是由理查德·班德勒和约翰·格林德在1976年创办的一门学问,美国前总统克林顿.微软领袖比尔盖茨.大导演斯皮尔博格等许多世界名人都接受过 NLP培训,世界500强企业中的 60% ...

  7. 李善友《认知升级之第一性原理》--507张PPT全解!_搜狐科技_搜狐网

      http://www.sohu.com/a/151470602_733114

  8. activity 概念认知

    工作流生命周期,5步 定义,工作流生命周期从流程定义开始. 发布,由开发人员打包各种资源,然后在系统管理中发布流程定义.包含流程定义文件.自定义表单.任务监听等. 执行,有具体的流程引擎如 activ ...

  9. 对于Mobile模块化的概念认知(小白)

    最近刚刚学习了Mobile的一些基础知识,把它整理一下方便自己的学习 那什么是Mobile呢? 自己的理解是将一个项目中共同的部分抽出来,这样就形成了Mobile模块. 为什么要使用Mobile呢? ...

随机推荐

  1. 第七课 GDB调试 (下)

    1序言: 通过前面一节第六课 GDB调试 (下)文章,可以掌握理解了gdb调试:怎么启动.运行,打断点.查看变量.甚至改变变量等的知识,今天来大概讲解下调试bug的类型. 2知识点: 2.1 就像之前 ...

  2. HDU - 6386 Age of Moyu 2018 Multi-University Training Contest 7 (Dijkstra变型)

    题意:N个点M条边的无向图,每条边都有属于自己的编号,如果一条路径上的边编号都相同,那么花费仅为1:改变至不同编号的路径,花费加1,无论这个编号之前是否走过. 分析:记录每个点的最小花费,再用set维 ...

  3. LeetCode 53. Maximum Subarray 最大连续字段和问题

    考察:最大连续字段和问题. 解决问题时间复杂度:O(n) 问题隐含条件:如果给出的数集都是负数,那么最大连续字段和就是,最大的那个负数. eg:{-2,-1}  结果应该输出 -1 而不是 0 int ...

  4. WPF使用Expression Design设计图形

    1.将画好的图形通过菜单导出成WPF xaml格式. 2.导出的文件就可以直接在WPF程序中使用了. 这里导出的DrawingBrush, <?xml version="1.0&quo ...

  5. sublime批量替换文本重复单词

    事先需要把单词打到文本的每一行 排序 按F9或者选择菜单:Edit > Sort Lines,对每行文本进行排序 查找重复行 排序好后,按Ctrl+F,调出查找面板 查找字符串: ^(.+)$[ ...

  6. JavaScript传递的是引用的副本

    看例子 var test1 = function (args) { args.name = "lcc2"; args = { name: "lcc3" }; } ...

  7. Tomcat 优化相关知识

    ---------(Tomcat Listener)----------- Tomcat 性能的因素是内存泄露.Server标签中可以配置多个Listener,其中 JreMemoryLeakPrev ...

  8. 20145201 《Java程序设计》第三周学习总结

    20145201 <Java程序设计>第三周学习总结 教材学习内容总结 本周学习了课本第四.五章内容,即认识对象和对象封装. 第四章 认识对象 4.1类与对象 定义类 要产生对象必须先定义 ...

  9. IE6+以上清除浮动普遍方法总结

    浮动,CSSfloat属性.学过的人应该知道这个属性,平时用的应该也是很多的.特别是在N栏布局中. 但是我们会经常遇到这样一种情况,前面的元素浮动之后会影响后面的元素,后面的元素需要用清除浮动来消灭前 ...

  10. java socket - 传递对象

    Person类: package com.zhyea.olproxy.socket; import java.io.Serializable; public class Person implemen ...