《C++ Primer 4th》读书笔记

C++ 的输入/输出(input/output)由标准库提供。标准库定义了一族类型,支持对文件和控制窗口等设备的读写(IO)。还定义了其他一些类型,使 string对象能够像文件一样操作,从而使我们无须 IO 就能实现数据与字符之间的转换。

IO 类型在三个独立的头文件中定义:iostream 定义读写控制窗口的类型,fstream 定义读写已命名文件的类型,而 sstream 所定义的类型则用于读写存储在内存中的 string 对象。

Header

Type

iostream

istream 从流中读取

ostream 写到流中去

iostream 对流进行读写;从   istream 和 ostream 派生而来

fstream

ifstream 从文件中读取;由   istream 派生而来

ofstream 写到文件中去;由   ostream 派生而来

fstream 读写文件;由   iostream 派生而来

sstream

istringstream 从   string 对象中读取;由 istream 派生而来

ostringstream 写到   string 对象中去;由 ostream 派生而来

stringstream 对 string   对象进行读写;由 iostream 派生而来

如果函数有基类类型的引用形参时,可以给函数传递其派生类型的对象。这就意味着:对 istream& 进行操作的函数,也可使用 ifstream 或者istringstream 对象来调用。

国际字符的支持

迄今为止,所描述的流类(stream class)读写的是由 char 类型组成的流。此外,标准库还定义了一组相关的类型,支持 wchar_t 类型。每个类都加上“w”前缀,以此与 char 类型的版本区分开来。每一个 IO 头文件都定义了 char 和 wchar_t 类型的类和标准输入/输出对象。

标准库类型不允许做复制或赋值操作。只有支持复制的元素类型可以存储在 vector 或其他容器类型里。由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)。第二个含义是:形参或返回类型也不能为流类型。如果需要传递或返回 IO对象,则必须传递或返回指向该对象的指针或引用。

一般情况下,如果要传递 IO 对象以便对它进行读写,可用非 const 引用的方式传递这个流对象。对 IO 对象的读写会改变它的状态,因此引用必须是非const 的。

IO 标准库管理一系列条件状态(condition state)成员,用来标记给定的 IO 对象是否处于可用状态,或者碰到了哪种特定的错误。

strm::iostate

机器相关的整型名,由各个 iostream   类定义,用于定义条件状态

strm::badbit

strm::iostate   类型的值,用于指出被破坏的流。标志着系统级的故障,如无法恢复的读写错误。

strm::failbit

strm::iostate   类型的值,用于指出失败的 IO 操作。可恢复的错误,如在希望获得数值型数据时输入了字符。

strm::eofbit

strm::iostate   类型的值,用于指出流已经到达文件结束符。是在遇到文件结束符时设置的,此时同时还设置了 failbit。

s.eof()

如果设置了流 s 的 eofbit   值,则该函数返回 true

s.fail()

如果设置了流 s 的 failbit   值,则该函数返回 true

s.bad()

如果设置了流 s 的 badbit   值,则该函数返回 true

s.good()

如果流 s 处于有效状态,则该函数返回   true

s.clear()

将流 s 中的所有状态值都重设为有效状态

s.clear(flag)

将流 s   中的某个指定条件状态设置为有效。flag 的类型是strm::iostate

s.setstate(flag)

给流 s 添加指定条件。flag 的类型是   strm::iostate

s.rdstate()

返回流 s 的当前条件,返回值类型为   strm::iostate

流必须处于无错误状态,才能用于输入或输出。检测流是否用的最简单的方法是检查其真值:

if (cin)

// ok to use cin, it is in a valid state

while (cin >> word)

// ok: read operation successful ...

int ival;

// read cin and test only for EOF; loop is executed even if there are other IO failures

while (cin >> ival, !cin.eof()) {

if (cin.bad()) // input stream is corrupted; bail out

throw runtime_error("IO stream corrupted");

if (cin.fail()) { // bad input

cerr<< "bad data, try again"; // warn the user

cin.clear(istream::failbit); // reset the stream

continue; // get next input

}

// ok to process ival

}

循环条件使用了逗号操作符。回顾逗号操作符的求解过程:首先计算它的每一个操作数,然后返回最右边操作数作为整个操作的结果

多种状态的处理,是使用按位或(OR)操作符在一次调用中生成“传递两个或更多状态位”的值。

// sets both the badbit and the failbit

is.setstate(ifstream::badbit | ifstream::failbit);

输出缓冲区的管理

每个 IO 对象管理一个缓冲区,用于存储程序读写的数据。如有下面语句:

os << "please enter a value: ";

系统将字符串字面值存储在与流 os 关联的缓冲区中。下面几种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件:

1. 程序正常结束。作为 main 返回工作的一部分,将清空所有输出缓冲区。

2. 在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前刷新。

3. 用操纵符显式地刷新缓冲区,例如行结束符 endl。

4. 在每次输出操作执行完后,用 unitbuf 操作符设置流的内部状态,从而清空缓冲区。

5. 可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流时将刷新其关联的输出缓冲区。

cout << "hi!" << flush; // flushes the buffer; adds no data

cout << "hi!" << ends; // inserts a null, then flushes the buffer

cout << "hi!" << endl; // inserts a newline, then flushes the buffer

如果需要刷新所有输出,最好使用 unitbuf 操纵符。这个操纵符在每次执行完写操作后都刷新流:

cout << unitbuf << "first" << " second" << nounitbuf;

nounitbuf 操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。

警告:如果程序崩溃了,则不会刷新缓冲区

tie 函数可用 istream 或 ostream 对象调用,使用一个指向 ostream 对象的指针形参。调用 tie 函数时,将实参流绑在调用该函数的对象上。如果一个流调用 tie 函数将其本身绑在传递给 tie 的 ostream 实参对象上,则该流上的任何 IO 操作都会刷新实参所关联的缓冲区。一个 ostream 对象每次只能与一个 istream 对象绑在一起。如果在调用

tie 函数时传递实参 0,则打破该流上已存在的捆绑。

cin.tie(&cout); // illustration only: the library ties cin and cout for us

ostream *old_tie = cin.tie();

cin.tie(); // break tie to cout, cout no longer flushed when cin is read

文件的输入和输出

fstream 类型除了继承下来的行为外,还定义了两个自己的新操作—— open和 close,以及形参为要打开的文件名的构造函数。

需要读写文件时,则必须定义自己的对象,并将它们绑定在需要的文件上。为 ifstream 或者 ofstream 对象提供文件名作为初始化式,就相当于打开了特定的文件。

// construct an ifstream and bind it to the file named ifile

ifstream infile(ifile.c_str());

// ofstream output file object to write file named ofile

ofstream outfile(ofile.c_str());

警告:C++ 中的文件名

由于历史原因,IO 标准库使用 C 风格字符串(第 4.3 节)而不是 C++ strings 类型的字符串作为文件名。在创建 fstream 对象时,如果调用open 或使用文件名作初始化式,需要传递的实参应为 C 风格字符串,而不是标准库 strings 对象。程序常常从标准输入获得文件名。通常,比较好的方法是将文件名读入 string 对象,而不是 C 风格字符数组。假设要使用的文件名保存在 string 对象中,则可调用 c_str 成员获取 C 风格字符串。

打开文件后,通常要检验打开是否成功,这是一个好习惯:

// check that the open succeeded

if (!infile) {

cerr << "error: unable to open input file: "

<< ifile << endl;

return -;

}

fstream 对象一旦打开,就保持与指定的文件相关联。如果要把 fstream 对象与另一个不同的文件关联,则必须先关闭(close)现在的文件,然后打开(open)另一个文件。

ifstream infile("in"); // opens file named "in" for reading

infile.close(); // closes "in"

infile.open("next"); // opens file named "next" for reading

如果遇到文件结束符或其他错误,将设置流的内部状态,以便之后不允许再对该流做读写操作。关闭流并不能改变流对象的内部状态。如果最后的读写操作失败了,对象的状态将保持为错误模式,直到执行 clear 操作重新恢复流的状态为止。调用 clear 后,就像重新创建了该对象一样。如果需要重用文件流读写多个文件,必须在读另一个文件之前调用 clear 清除该流的状态。

ifstream input;

vector<string>::const_iterator it = files.begin();

// for each file in the vector

while (it != files.end()) {

input.open(it->c_str()); // open the file

// if the file is ok, read and "process" the input

if (!input)

break; // error: bail out!

while(input >> s) // do the work on this file

process(s);

input.close(); // close file when we're done with it

input.clear(); // reset state to ok

++it; // increment iterator to get next file

}

文件模式

在打开文件时,无论是调用 open 还是以文件名作为流初始化的一部分,都需指定文件模式(file mode)。ate 模式只在打开时有效:文件打开后将定位在文件尾。以 binary 模式打开的流则将文件以字节序列的形式处理,而不解释流中的字符。

in

打开文件做读操作

out

打开文件做写操作

app

在每次写之前找到文件尾

ate

打开文件后立即将文件定位在文件尾

trunc

打开文件时清空已存在的文件流

binary

以二进制模式进行 IO 操作

对于用 ofstream 打开的文件,要保存文件中存在的数据,唯一方法是显式地指定 app 模式打开:

// output mode by default; truncates file named "file1"

ofstream outfile("file1");

// equivalent effect: "file1" is explicitly truncated

ofstream outfile2("file1", ofstream::out | ofstream::trunc);

// append mode; adds new data at end of existing file named "file2"

ofstream appfile("file2", ofstream::app);

默认情况下,fstream 对象以 in 和 out 模式同时打开。当文件同时以 in和 out 打开时不清空。

只要调用 open 函数,就要设置文件模式,其模式的设置可以是显式的也可以是隐式的。如果没有指定文件模式,将使用默认值。

一个打开并检查输入文件的程序

// opens in binding it to the given file

ifstream& open_file(ifstream &in, const string &file)

{

in.close(); // close in case it was already open

in.clear(); // clear any existing errors

// if the open fails, the stream will be in an invalid state

in.open(file.c_str()); // open the file we were given

return in; // condition state is good if open succeeded

}

字符串流

将流与存储在程序内存中的string 对象捆绑起来。

stringstream strm;

创建自由的 stringstream 对象

stringstream strm(s);

创建存储 s 的副本的   stringstream 对象,其中 s 是string 类型的对象

strm.str()

返回 strm 中存储的 string   类型对象

strm.str(s)

将 string 类型的 s 复制给   strm,返回 void

string line, word; // will hold a line and word from input,

respectively

while (getline(cin, line)) { // read a line from the input into line

// do per-line processing

istringstream stream(line); // bind to stream to the line we read

while (stream >> word){ // read a word from line

// do per-word processing

}

}

stringstream 提供的转换和/或格式化

stringstream 对象的一个常见用法是,需要在多种数据类型之间实现自动格式化时使用该类类型

int val1 = , val2 = ;

ostringstream format_message;

// ok: converts values to a string representation

format_message << "val1: " << val1 << "\n"

<< "val2: " << val2 << "\n";

这里创建了一个名为 format_message 的 ostringstream 类型空对象,并将指定的内容插入该对象。重点在于 int 型值自动转换为等价的可打印的字符串

// str member obtains the string associated with a stringstream

istringstream input_istring(format_message.str());

string dump; // place to dump the labels from the formatted message

// extracts the stored ascii values, converting back to arithmetic

types

input_istring >> dump >> val1 >> dump >> val2;

cout << val1 << " " << val2 << endl; // prints 512 1024

str 成员获取与之前创建的 ostringstream 对象关联的string 副本。再将 input_istring 与 string 绑定起来。在读 input_istring时,相应的值恢复为它们原来的数值型表示形式

C++ 标准IO库的更多相关文章

  1. [APUE]标准IO库(下)

    一.标准IO的效率 对比以下四个程序的用户CPU.系统CPU与时钟时间对比 程序1:系统IO 程序2:标准IO getc版本 程序3:标准IO fgets版本 结果: [注:该表截取自APUE,上表中 ...

  2. [APUE]标准IO库(上)

    一.流和FILE对象 系统IO都是针对文件描述符,当打开一个文件时,即返回一个文件描述符,然后用该文件描述符来进行下面的操作,而对于标准IO库,它们的操作则是围绕流(stream)进行的. 当打开一个 ...

  3. 文件IO函数和标准IO库的区别

    摘自 http://blog.chinaunix.net/uid-26565142-id-3051729.html 1,文件IO函数,在Unix中,有如下5个:open,read,write,lsee ...

  4. C++ Primer 读书笔记: 第8章 标准IO库

    第8章 标准IO库 8.1 面向对象的标准库 1. IO类型在三个独立的头文件中定义:iostream定义读写控制窗口的类型,fstream定义读写已命名文件的类型,而sstream所定义的类型则用于 ...

  5. 高级UNIX环境编程5 标准IO库

    标准IO库都围绕流进进行的 <stdio.h><wchar.h> memccpy 一般用汇编写的 ftell/fseek/ftello/fseeko/fgetpos/fsetp ...

  6. c++ primer 学习杂记3【标准IO库】

    第8章 标准IO库 发现书中一个错误,中文版p248 流状态的查询和控制,举了一个代码例子: int ival; // read cin and test only for EOF; loop is ...

  7. 18、标准IO库详解及实例

    标准IO库是由Dennis Ritchie于1975年左右编写的,它是Mike Lestbain写的可移植IO库的主要修改版本,2010年以后, 标准IO库几乎没有进行什么修改.标准IO库处理了很多细 ...

  8. 第十三篇:带缓冲的IO( 标准IO库 )

    前言 在之前,学习了 read write 这样的不带缓冲IO函数. 而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 wri ...

  9. C5 标准IO库:APUE 笔记

    C5 :标准IO库 在第三章中,所有IO函数都是围绕文件描述符展开,文件描述符用于后续IO操作.由于文件描述符相关的操作是不带缓冲的IO,需要操作者本人指定缓冲区分配.IO长度等,对设备环境要求一定的 ...

  10. 带缓冲的IO( 标准IO库 )

    前言 在之前,学习了 read write 这样的不带缓冲IO函数.而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 writ ...

随机推荐

  1. spring集成freemaker 制作短信模板

    1.配置configure的Bean,Bean中指定了模板文件的路径和刷新时间等配置. <!-- 配置freeMarkerConfigurer进行属性值的注入 --> <bean i ...

  2. nullcon HackIM 2016 -- Programming Question 1

    So you reached Delhi and now the noise in your head is not allowing you to think rationally. The Nos ...

  3. JNI Local Reference Changes in ICS

    [This post is by Elliott Hughes, a Software Engineer on the Dalvik team. — Tim Bray] If you don’t wr ...

  4. linux shell 去重 uniq和sort的区别

    sort -u 和 uniq都能起到删除重复信息的功能,那么他们的区别究竟在哪呢?$ cat test              jasonjasonjasonfffffjason 下面分别执行三个命 ...

  5. POJ 3180-The Cow Prom (图论-有向图强联通tarjan算法)

    题目大意:有n个牛在一块, m条单项绳子, 有m个链接关系, 问有多少个团体内部任意两头牛可以相互可达 解题思路:有向图强连通分量模版图 代码如下: #include<stdio.h> # ...

  6. UVA315 (无向图求割点)

    题目大意:给定一个无向图,问共存在多少个割点.(割点:去掉此点后此图会断开连接)割点有两种存在:一种是第一次搜索的根节点,若其子节点数超过两个,则此点去掉后图会 断开连接,因此此点为割点:或者此点为搜 ...

  7. SAP 禁止某个库位的货物移动

    SAP 禁止某个库位的货物移动 1.先去spro --> 物料管理 --> 库存管理和实际库存 --> 权限管理 --> 授权检查存储位置 将要禁止的库位后的权限勾选上, 2. ...

  8. git 使用命令总结

    当远程仓库有README.md的时候我们创建的工程里没有这个文件这时候你提交的仓库和远程的不一致就会导致提交被拒绝 此时我们可以先拉取主干到本地的temp分支  然后和本地的master分支合并 通过 ...

  9. locutus(phpjs) 的使用

    今天来介绍一个js的框架,这个框架的主要功能呢,是通过加载该类库,来实现php函数的调用 当然了,这并不是说php中所有的函数都能在js中使用,但很大一部分是可以的. 环境:mac + node v5 ...

  10. 深入浅出Symfony2 - 如何提高网站响应速度 [转]

    简介 Symfony2是一个基于PHP语言的Web开发框架,有着开发速度快.性能高等特点.但Symfony2的学习曲线也比较陡峭,没有经验的初学者往往需要一些练习才能掌握其特性.相对其他框架,Symf ...