我发现我非常容易忘记C和C++中有关IO的东西诶,估计是被python惯坏了,决定趁热把C和C++中IO相关的东西总结下,如果以后忘了还可以回来看看。

C和C++程序都把输入和输出看作是字节流。即输入的时候从输入流中抽取字节;输出的时候将字节放入到流中。输入流中的字节可能来自键盘,也可能来自存储设备(如硬盘)或其他程序。同样,输出流中的字节可以流向屏幕、打印机、存储设备或者其他程序。
这里流充当了程序和流源或者流目标之间的桥梁
C/C++程序只是检查字节流就好了,这使得程序可以以相同方式处理来自不同的输入源,程序不需要知道这字节流是最初是来自哪里的,输出流也是如此。
因此要管理输入和输出需要两步:

  1. 将流域输入和输出去向的程序关联起来
  2. 将流与文件关联起来

图片来自网络,非原创
这里可以做个比较好的比喻就是:可以把C/C++程序看成是在河边喝水的我,对于我来说,水就是从河里喝到的,这是我得到水的唯一方式,但是河里的水可能从任何地方来,也能到任何地方去。

缓冲区

通过使用缓冲区可以更高效的处理输入和输出。缓冲区是用作中介的内存块,是将信息从数据源传输到程序或者程序到目标数据设备的临时存储工具。通常程序都是一个字节一个字节的接受和发出数据的,而磁盘驱动器这样的设备是以512Bytes或者更多字节这样一块一块传输数据的。缓冲区变为这种吞吐量的不匹配做缓冲。这样先把文件中的数据放到缓冲区中,然后程序从缓冲区中一个字节一个字节的去读取数据。当把缓冲区中的数据读取完毕后,会再从文件中读取下一批数据。。。

这就好像水库在暴风雨中收集了大量的水,由于人喝水不可能接受那么大的水量,所以先通过水库缓冲,水库用较慢的速度将水发放给人类喝,或者说放到流中,然后人从流中一口一口的喝。

图片来自网络,非原创

C/C++对流和缓冲区的管理

通过对流进行管理便可以管理程序对数据的输出和输入。

  • C语言通过FILE结构体以及标准库函数来对流中的数据的读取和插入进行管理,
  • C++则是使用定义在标准库中的类(如istream, ifstream…等)来对流和数据的抽取和插入进行管理。


C语言中使用FILE结构体(或者其指针)

通常使用标准I/O的第一步就是使用fopen()函数打开一个文件(其中stdin, stderr, stdout文件时自动打开的)。当使用fopen()打开一个文件的时候,不仅打开了文件,而且还创建了缓冲区(如果是读写模式将创建两个缓冲区),同时还创建了一个包含文件和缓冲区相关信息的结构体,然后返回此结构体的指针。这个结构体就是FILE结构。
我们就称fopen()打开了一个流(文本流或者二进制流)

这个FILE结构体都包含的信息:

  • 文件位置指示器(确定在流中当前的位置)
  • 错误指示器
  • 文件结尾指示器
  • 一个指向缓冲区起始处的指针
  • 一个文件标志符
  • 一个记录实际复制到缓冲区中的字节数的计数器。

通过标准库函数来操作流中的数据

  1. 正常创建流以后会通过标准库中的输入函数来获取数据,比如fscanf(), getc(), fgets()调用这些函数中的任意一个都会把一块数据从文件复制到缓冲区中,除了填充缓冲区外,第一次调用读取数据的函数还会设置FILE中的一些信息,例如流中当前的位置(默认从字节0开始)。
  2. 由于所有输入函数都是用用一个缓冲区,所以任何一个被调用的函数都将在前一次函数调用停止的地方(流中)继续开始。
  3. 当缓冲区的数据全部被程序读取完时,再请求下一块数据填入到缓冲区中,知道读入文件中的全部内容。
  4. FILE中的文件结尾指示器设为true,于是下一个使用该结构体的函数将返回EOF

输出的方式类似,当缓冲区填满时,将数据恢复到文件中。


C++使用iostream对象

iostream文件中包含了专门设计用来实现管理流和缓冲区的类。

  • streambuf类为缓冲区提供了内存,并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲区和管理缓冲区内存的类方法
  • ios_base类表示流的一般特征,是否可读取、是二进制流还是文本流
    这个类也存储了描述格式状态的信息,例如,一个类成员的某些为决定了使用的计数系统,而另一个成员则决定了字段宽度。
    同时这个类也有个成员函数setf()配合着他的静态常量例如ios_base::showpoint可以控制多种格式化特性。
  • ios类基于ios_base,其中包括了一个指向streambuf对象的指针成员,是不是有些像FILE结构体中的那个指向缓冲区内存的指针?
  • ostream类是从ios类派生来的,提供了想输出流中输出的方法
  • istream类也是从ios派生来的,提供了从流中输入的方法。
  • iostream类是多重继承于istreamostream类的,继承了他两个的所有方法。

通过创建一个这些类的对象来创建流,开辟缓冲区、并将其与流关联起来。

cout对象凭借着streambuf对象的帮助管理之流中的字节流

Comments

2016-03-16