《C++Primer》第八章 IO库

IO类

前面提到的IO类型和对象都是操纵char数据的,并且都是关联到用户的控制台窗口的。我们还有其他IO需求:

  • 除了从控制台进行IO操作,应用程序还经常需要读写文件
  • 除了操纵char数据还需要操纵string

为了支持上述操作,在istreamostream,标准库还定义了一些其他IO类型。分别定义在三个独立的头文件中:

  • iostream:定义了用于读写流的基本类型
  • fstream:定义了读写命名文件的类型
  • sstream:定义了读写内存string对象的类型

标准库通过继承机制inheritance是我们可以忽略不同类型的流之间的差异。比如ifstreamistringstream都继承自istream,因此我们可以像使用istream对象一样来使用ifstreamistringstream对象。我们是如何使用cin的也可以同样地使用这些类型的对象。比如可以对一个ifstreamistringstream对象调用getline,也可以使用>>从一个ifstreamistringstream对象中读取数据。

1. IO对象无拷贝或者赋值

我们不能拷贝或对IO对象赋值,因此我们也不能将形参或返回类型设置为流类型。进行IO操作时通常是以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用是非const的。

2. 条件状态

IO操作与生俱来的问题是可能发生错误,一些错误是可修复的,而其他错误则可能发生在系统深处超出了应用程序可以修正的范围。下面列出来IO类所定义的一些函数和标记:

  • strm::badbit:指出流已崩溃
  • strm::failbit:支持一个IO操作失败了
  • strm::eofbit:指出流到达了文件结束
  • strm::goodbit:指出流未处于崔武状态
  • s.eof():若流seofbit置位,则返回true
  • s.fail():若流sfailbitbadbit置位,则返回true
  • s.bad():若流sbadbit置位,则返回true
  • s.good():若流s处于有效状态,则返回true
  • s.clear():将流s的所有条件状态复位,将流的状态设置为有效,返回void
  • s.clear(flags):将流s的对应条件状态复位
  • s.setstate(flags):设置流s的对应条件状态

一个流一旦发生错误,其后续的IO操作都会失败,因此代码通常需要在使用一个流之前检查它是否处于良好状态,确定一个流对象的最简单方法是将它作为一个条件使用:

while (cin >> word)
    // ok: 读操作成功...

将流作为条件使用,只能告诉我们流是否有效而无法告诉我们具体发生了什么,我们有时候需要知道错误的具体原因以及是否能恢复。IO库定义了一个与机器无法的iostate类型:

  • badbit:表示系统级错误,一旦badbit被置位,流一般也无法使用了
  • failbit:发生可恢复错误时,failbit被置位,比如期望读取数值却读到一个字符
  • 到达文件结束时,eofbitfailbit都会被置位
  • goodbit:值为0表示流未发生错误,只要badbiteofbitfailbit中任一个被置位,则表示发生错误

使用fail()good()是确定流总体状态的正确方法,而eofbad操作用于确定具体的错误。

3. 管理输出缓冲

每一个输出流都管理一个缓冲区,比如执行输出代码时文本串可以被立即打印出来,也可能被操作系统保存在缓冲区中用于将多个输出操作组合为单一的系统级写操作。

这主要是因为设备的写操作可能很耗时,操作系统将多个输出操作组合成单一的设备写操作可以极大提升性能。

缓冲刷新,即数据真正写到输出设备或文件的原因有如下:

  • 程序正常结束:作为main函数的return操作的一部分,执行缓冲刷新
  • 缓冲区满时:刷新缓冲方便新的数据写入缓冲区
  • 使用操纵符endl来显式刷新缓冲区
  • 每个输出操作之后,我们可以使用操纵符unitbuf设置流的内部状态来清空缓冲区。默认情况下,对cerr是设置的unitbuf的,因此写到cerr的内容都是立即刷新的
  • 一个输出流可能被关联到另一个流,在这种情况下读写被关联的流时,关联到的流的缓冲区会被刷新,比如cincerr都关联到cout,读cin或写cerr都会导致cout的缓冲区被刷新

控制缓冲的操纵符:

  • endl:输出换行符并刷新缓冲区
  • flush:不附加任何额外字符,刷新缓冲区
  • ends:输出一个空字符并刷新缓冲区
  • unitbuf:所有输出操作后都立即刷新缓冲区
  • nounitbuf:回到正常的缓冲方式

需要注意的是,如果程序崩溃,输出缓冲区不会被刷新,调试一个已经崩溃的程序时,需要确认输出数据是不是因为被挂在缓冲区而没有打印

交互式系统都应该关联输入流和输出流,这意味着所有输出包括用户提示信息都会在读操作之前被打印出来。

文件输入输出

1. 类型及操作

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

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

上面提到的类型继承了cincout操作(比如>><<getline等),fstream还包括其他特有的操作:

  • fstream fstrm(s);:创建一个fstream并打开名为s的文件,其中s可以是string也可以是C风格字符串指针,这些构造函数都是explict
  • fstream fstrm(s, mode);:和前一个构造函数类似,但按指定模式打开文件
  • fstrm.open(s):打开名为s的文件,并将文件与fstrm绑定
  • fstrm.close():关闭与fstrm绑定的文件,并返回void
  • fstrm.is_open():判断与fstrm的文件是否成功打开且尚未关闭

2. 使用文件流对象

ifstream in(ifile); // 构造一个ifstream并打开给定文件
ofstream out;       // 构造输出文件流,并未关联到任何文件

在要求使用基类型对象的地方,我们可以用继承类型的对象代替,这意味着接受一个iostream类型引用(或指针)参数的函数可以用一个对应的fstreamsstream类型来调用。

如果我们定义了一个空文件流对象,随后可以用open来将它与文件关联起来:

ifstream in(ifile);   // 构造一个ifstream并打开文件
ofstream out;         // 输出文件流未与任何文件相关联
out.open(ifile + ".copy"); // 打开指定文件

// 如果调用open失败的话,`failbit`会被置位
if (out) // 检查open是否成功,成功的话我们就可以写入文件

一旦一个文件流已经打开,他就会保持与对应文件的关联,如果对一个恶已经打开的文件流调用open会失败,并会导致failbit被置位,因此文件流关联到另外一个文件时需要先关闭已关联的文件。

3. 自动构造和析构

  • 当一个fstream对象离开其作用域时,与之关联的文件会自动关闭
  • 当一个fstream对象被销毁时,close会自动被调用

4. 文件模式

  • in:读方式打开
  • out:写方式打开
  • app:每次写操作前均定位到文件末尾
  • ate:打开文件后立即定位到文件末尾
  • trunc:截断文件
  • binart:以二进制方式进行IO

ifstream关联的文件默认以in模式打开,与ofstream关联的文件默认以out模式打开,与fstream关联的文件默认以inout模式打开。

如果我们以out模式打开文件时文件的内容会被全部丢弃,阻止一个ofstream清空给定文件内容的方法是同时制定app模式:

// 下面几条语句中,file1都会被截断
ofstream out("file1");      // 默认以out模式打开
ofstream out("file1", ofstream::out);  // 隐含地截断文件
ofstream out("file1", ofstream::out | ofstream::trunc);
// 为了保留文件内容,必须显式指定app模式
ofstream app("file2", ofstream::app);  // 隐含为输出模式
ofstream app("file2", ofstream::out | ofstream::app);

保留被ofstream打开的文件中已有数据的唯一方法是显式制定appin模式。

string流

  • istringstream:从string读取数据
  • ostringstream:向string写入数据
  • stringstream:既可以从string中读数据,也可以向string写数据

stringstream特有的操作包括:

  • sstream strm(s)strm是一个sstream对象,保存string s的一个拷贝,此构造函数是explict
  • strm.str():返回strm所保存的string的拷贝
  • strm.str(s):将string s拷贝到strm中,返回void
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • 整理自《C++Primer》 第八章之前涉及到IO的内容 8.1 IO类 三个头文件9个io对象继承关系 基础IO...
    FakeCSer爱去网吧阅读 434评论 0 0
  • 第八章 IO库 8.1 IO类 IO类型和头文件如下: 如cin就是一个istream对象,用于从标准输入流中读取...
    带鱼去兜风阅读 643评论 0 0
  • 第8章:IO库 8.1 IO类 IO库类型和头文件 头文件类型iostreamistream,wistream从流...
    北冥有鱼wyh阅读 242评论 0 1
  • #1.IO类IO对象无拷贝或赋值条件状态管理输出缓冲 #2.文件输入输出使用文件流对象文件模式 #3.string...
    MrDecoder阅读 343评论 0 0
  • 8.1 标准库IO类 其中w开头的为wchar_t宽字符数据。 ifstream和istringstream继承自...
    陈小尧阅读 754评论 0 49