在C语言中,用printf和scanf进行输入输出,往往不能保证所输入输出的数据是可靠的安全的。在C++的输入输出中,编译系统对数据类型进行严格的检查,凡是类型不正确的数据都不可能通过编译。因此C++的I/O操作是类型安全(type safe)的。 C++的I/O操作是可扩展的,不仅可以用来输入输出标准类型的数据,也可以用于用户自定义类型的数据。C++对标准类型的数据和对用户声明类型数据的输入输出,采用同样的方法处理。C++通过I/O类库来实现丰富的I/O功能。
1. IO类
类 fstream 和 stringstream 都是继承自类 iostream 的。输入类都继承自 istream,输出类都继承自 ostream。因此,可以在 istream 对象上执行的操作,也可在 istream 或 istringstream 对象上执行。继承自 ostream 的输出类也有类似情况。
istream(输入流)类型,提供输入操作。
ostream(输出流)类型,提供输出操作。
cin,一个 istream 对象,从标准输入读取数据。
cout,一个 ostream 对象,向标准输出写入数据。
cerr,一个 ostream 对象,通常用于输出程序错误信息,写入到标准错误。
clog,clog流对象也是标准错误流,它是console log的缩写。它的作用和cerr相同,都是在终端显示器上显示出错信息。区别:cerr是不经过缓冲区,直接向显示器上输出有关信息,而clog中的信息存放在缓冲区中,缓冲区满后或遇endl时向显示器输出。
>>运算符,用来从一个 istream 对象读取输入数据。
<<运算符, 用来向一个 ostream 对象中写入输出数据。
getline 函数,从一个给定的 istream 读取一行数据,存入一个给定的 string 对象中。
eof 函数,从输入流读取数据,如果到达文件末尾(遇文件结束符),eof函数值为非零值(真),否则为0(假)。
peak 函数,peek是“观察”的意思,peek函数的作用是观测下一个字符。其调用形式为: c=cin.peek( ), 函数的返回值是指针指向的当前字符,但它只是观测,指针仍停留在当前位置,并不后移。如果要访问的字符是文件结束符,则函数值是EOF(-1)。
putback 函数,其调用形式为 cin.putback(ch),其作用是将前面用get或getline函数从输入流中读取的字符ch返回到输入流,插入到当前指针位置,以供后面读取。
ignore 函数,其调用形式为 cin.ignore(n, 终止字符),函数作用是跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此时跳过包括终止字符在内的若干字符),也可以不带参数或只带一个参数,如:
cin.ignore() // n默认值为1,终止字符默认为EOF, 相当于ignore(1, EOF)
IO库条件状态
strm::iostate strm 是一种IO类型,iostate 是一种机器相关的类型,提供了表达条件状态的完整功能
strm::badbit 用来指出流已崩溃
strm::failbit 用来指出一个IO操作失败了
strm::eofbit 用来指出流到达了文件结束
strm::goodbit 用来指出流未处于错误状态,此值保证为0
s.eof() 若流 s 的 eofbit 置位,则返回 true
s.fail() 若流 s 的 failbit 或 badbit 置位,则返回 true
s.bad() 若流 s 的 badbit 置位,则返回 true
s.good() 若流 s 处于有效状态,则返回 true
s.clear() 将流 s 中所有条件状态复位,将流的状态设置为有效,返回void
s.clear(flags) 根据给定的flag标志位,将流 s 中对应条件状态复位,flags 的类型为 strm::iostate。返回void
s.setstate(flags) 根据给定的flag标志位,将流 s 中对应条件状态置位,flags 的类型为 strm::iostate。返回void
s.rdstate() 返回流 s 的当前条件状态,返回值类型为 strm::iostate
endl操作符: 换行并刷新缓冲区,cout << endl;
flush操作符: 刷新缓冲区,不输出任何额外的字符,cout << flush;
ends操作符: 输出一个空字符并刷新缓冲区,cout << ends;
unitbuf操作符: cout << unitbuf; // 所有输出操作后都会立即刷新缓冲区,无缓冲。
nounitbuf操作符: cout << nounitbuf; // 重置流,恢复使用正常系统管理的缓冲区刷新机制
2. 文件输入输出
头文件 fstream 定义了三个类型来支持文件IO: ifstream 从一个给定文件读取数据, ofstream 向一个给定文件写入数据,以及 fstream 可以读写给定文件。
fstream 特有的操作
fstream fstrm; 创建一个未绑定的文件流, fstream 是头文件 fstream 中定义的一个类型
fstream fstrm(s); 创建一个 fstream, 并打开名为 s 的文件, s 可以是 string 类型,或者指向字符串的指针。这些构造函数都是 explicit 的,默认文件模式依赖于 fstream 的类型
fstream fstrm(s, mode); 与前一个构造函数类似,但按指定 mode 打开文件
fstrm.open(s); 打开名为 s 的文件, 并将文件与fstrm 绑定,s 可以是 string 类型,或者指向字符串的指针。这些构造函数都是 explicit 的,默认文件模式依赖于 fstream 的类型,返回 void
fstrm.close(); 关闭与 fstrm 绑定的文件,返回 void
fstrm.is_open(); 返回一个 bool 值,指出与 fstrm 关联的文件是否成功打开且尚未关闭
文件模式(mode)
in 以读方式打开
out 以写方式打开,隐含截断文件
app 每次写操作前均定位到文件末尾,隐含输出模式
ate 打开文件后立即定位到文件末尾
trunc 截断文件
binary 以二进制方式进行IO
// example8_8.cpp 追加写入文件示例
#include <iostream>
#include <fstream>
#include "Sales_data.h"
using namespace std;
int main(int argc, char *argv[])
{
if (argc != 3) {
cerr << "请给出输入、输出文件名" << endl;
return -1;
}
ifstream in(argv[1]);
if (!in) {
cerr << "无法打开输入文件" << endl;
return -1;
}
ofstream out(argv[2], ofstream::app);
if (!out) {
cerr << "无法打开输出文件" << endl;
return -1;
}
Sales_data total; // 保存当前求和结果的变量
if (read(in, total)) { // 读入下一笔交易
Sales_data trans; // 保存下一条交易数据的变量
while (read(in, trans)) { // 读入剩余的交易
if (total.isbn() == trans.isbn()) { // 检查isbn
total.combine(trans); // 更新变量total当前的值
} else {
print(out, total) << endl; // 输出结果
total = trans; // 处理下一本书
}
}
print(out, total) << endl; // 输出最后一条交易
} else { // 没有输入任何信息
cerr << "没有数据" << endl; // 通知用户
}
return 0;
}
3. string流
sstream 头文件定义了三个类型来支持内存IO,istringstream 从 string 读取数据,ostringstream向 string 写入数据,stringstream 既可从 string 读数据,也可向 string 写数据,就像 string 是一个IO流一样。
stringstream 特有的操作
sstream strm; strm 是一个未绑定的 stringstream 对象。stream 是头文件 sstream 中定义的一个类型
sstream strm(s); strm 是一个 sstream 对象,保存 string s 的一个拷贝,此构造函数是 explicit 的
strm.str(); 返回 strm 所保存的 string 的拷贝
strm.str(s); 将 string s 拷贝到 strm 中,返回 void
// example8_13.cpp 测试 stringstream
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
struct PersonInfo {
string name;
vector<string> phones;
};
string format(const string &s) {
return s;
}
bool valid(const string &s) {
return true;
}
int main(int argc, char *argv[])
{
string line, word; // 分别保存来自输入的一行和单词
vector<PersonInfo> people; // 分别保存来自输入的所有记录
istringstream record;
if (argc != 2) {
cerr << "请给出文件名" << endl;
return -1;
}
ifstream in(argv[1]);
if (!in) {
cerr << "无法打开输入文件" << endl;
return -1;
}
while (getline(in, line)) {
PersonInfo info; // 创建一个保存此记录
record.clear(); // 重复使用字符串流时,每次都要调用clear
record.str(line); // 将记录绑定到刚读入的行
record >> info.name; // 读取名字
while (record >> word) { // 读取电话号码
info.phones.push_back(word); // 保存它们
}
people.push_back(info); // 将此记录追加到people末尾
}
ostringstream os;
for (const auto &entry : people) { // 对people中每一项
ostringstream formatted, badNums; // 每个循环步创建的对象
for (const auto &nums : entry.phones) { // 对每个数
if (!valid(nums)) {
badNums << " " << nums; // 将数的字符串形式存入badNums
} else {
// 将格式化的字符串“写入”formatted
formatted << " " << format(nums);
}
}
if (badNums.str().empty()) { // 没有错误的数
// 打印名字和格式化的数
os << entry.name << " " << formatted.str() << endl;
} else {
// 打印名字和错误的数
cerr << "input error: " << entry.name
<< " invalid number(s) " << badNums.str() << endl;
}
}
cout << os.str() << endl;
return 0;
}