C++ IO

I/O类

标准库提供了三类IO操作,它们分别是读写流的iostream,读写文件的fstream,读写内存中string的sstream。如下表所示:
io
ifstream和istringstrem都继承自istream,ofstream和ostringstream都继承自ostream。像使用cin和cout那样使用它们就行。

IO对象的特性

  1. 不能拷贝或者对IO对象赋值。所以不能将形参和返回类型设置为流类型,必须将它们设置为流引用类型。
  2. 读写一个IO对象会改变它的状态,因此传递和返回的引用不能是const的。

IO流的状态

IO操作很容易出错,一些错误是可恢复的,另一些是不可恢复的。下面是IO类中定义的函数和表示,可以帮助我们访问和操纵流的状态。
condition
strm::iostate中存放了当前IO流的状态,这个类型是一个位集合,IO类定义了四个iostate类型的常量表达式表达特定的位类型,可以使用位运算与设置或者检测多个标志位:

  • strm::badbit,表示系统级的错误
  • strm::failbit,可恢复错误(到达文件结束也会置位strm::failbit,发生系统级错误时也会被置位)
  • strm::eofbit,到达文件结束
  • strm::goodbit,流处于未出错状态

它们用来表示流的状态,可以用good(), fail()eof(), bad()分别查询对应标志位的状态。我们将流当做条件使用的代码其实就是使用的是状态位的状态。
可以使用rdstate函数获得当前流的状态,使用setstate对给定条件位置位,使用clear可以清除所错误标志位,也可以清除指定错误标志位。

管理缓冲区

关于缓冲区的内容,简单来说,缓冲区的作用就是减少系统级IO,提高读写效率。具体介绍可以查看
这里介绍一下导致C++中缓冲区刷新的原因:

  1. 程序结束,作为main函数return的一部分,冲洗缓冲区
  2. 缓冲区满时,冲洗缓冲区
  3. 使用操作符endl,会在输出内容的末尾加一个'\n',然后刷新缓冲区;使用flush刷新缓冲区,不附加任何字符;使用ends在输出内容的末尾加一个空字节,并不会刷新缓冲区(C++ Primer第五版上写错了)。
  4. 使用cout << unitbuf设置为每次输出操作后都刷新缓冲区(即使不适用endl等操作符),即无缓冲,使用cout << nounitbuf恢复。
  5. 一个输出流可能关联到另一个流,当读写关联的流时,关联到的流的缓冲区都会被刷新。

iostream

C++标准IO库iostream提供了输入流istream和输出流ostream,一个流就是一个字符序列,从IO设备中读出或者写入IO设备。

标准输入输出对象

  • cin,标准输入
  • cout,标准输出
  • cerr,标准错误
  • clog,用来输出一些普通信息。

通常系统会将程序所运行的窗口和标准IO对象关联起来,读取cin,从当前程序关联的窗口进行读取,向cout,cerrclog写入数据时,会写到同一个窗口中,可对它们进行重定向。

输入输出运算符

  • <<输出运算符,接收两个运算符,左侧需要是ostream对象,右侧需要是要打印的对象。返回左侧运算对象,即写入给定值的ostream对象。
  • >>输入运算符,接收两个运算符,左侧需要是istream对象,右侧从istream中读入的数据要写入的对象。返回左侧运算对象,即给定的istream对象。

一直有个问题就是为什么<<是输出,>>是输入,可以简单的把箭头方向当做数据流向,输出的数据流向ostream中的标准输出cin,输入时,数据从istream的标准输入cin流向变量。

操纵符

endl是一种操纵符。当把它写入到ostream的时候,有两个作用:

  1. 结束当前行
  2. 将与当前输出设备相关的缓冲区中的内容刷新到输出设备中。这个刷新操作可以保证目前为止程序的所有输出都真正写入输出流而不是在内存中的等待写入输出流。

命名空间

命名空间可以解决名自定义冲突问题。比如有两个不同的库中实现了一个同名的函数,可以通过加上命名空间进行区分。cin,coutendl都定义在std命名空间中。在访问时需要使用以下方式:std::cinstd::coutstd::endl
C++标准库定义的名字在都在std中。

读取任意的输入数据

下面的示例代码对任意的输入数据进行求和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>

using std::cin;
using std::endl;
using std::cout;

int main()
{
int sum = 0, value = 0;
while(std::cin >> value)
{
sum += value;
}

std::cout << "Sum is: " << sum << std::endl;

return 0;
}

fstream

头文件fstream定义了三个类型支持文件IO:

  • ifstream从一个给定的文件读取数据;
  • ofstream向一个给定的文件写入数据;
  • fstream读写给定文件。

这些类型和我们之前使用的cincout一样,可以使用getline从一个ifstream中读取数据。fstream具有以下的一些特殊操作:
fstream
这些操作只有fstream,ofstreamifstream对象能调用,其他类型不行。

使用fstream,ifstreamofstream

我们想要读文件的时候,可以定义一个fstream对象,然后将这个对象和文件关联起来,每个fstream类都定义了一个名字为open的成员函数,它完成了一些系统相关的操作,定位给定的文件,打开为读或写模式。
在创建fstream对象时,我们可以提供一个文件名,此时open函数会被自动调用。如果定义了一个空fstream对象,可以手动调用open将它和一个文件关联起来。调用open可能失败,进行open是否成功的检测通常是一个好习惯。
close函数可以关闭fstream当前关联的文件,当一个fstream对象被销毁时,会自动调用close函数。

1
2
3
4
5
6
// 1.构造一个ifstream并打开给定文件,文件名可以是string,也可以是C风格字符串
ifstream in(ifile);

// 2.输出文件流并未关联到任何文件。
ofstream out;
out.open(ifile+".out");

可以使用fstream代替iostream,因为fstreamiostream的子类。

file mode

C++ 中的file mode如下所示:
file_mode
具体的使用可以等用到的时候查资料。

sstream

头文件sstream定义了三个类型支持内存IO:

  • istringstreamstring读取数据;
  • ostringfstreamstring写入数据;
  • stringstream读写string

stringstream特有的操作如下:
stringstream

使用istringstream和使用ostringstream

最好的就是写一个例子。

参考文献

1.《C++ Primer第五版》