第八章 IO库

8.1 IO类

  1. C++使用标准库类来处理面向流的输入和输出:
    iostream处理控制台IO。
    fstream处理命名文件IO。
    stringstream完成内存string的IO。
    在这里插入图片描述

8.1.1 IO对象无拷贝或赋值

  1. 由于不能拷贝IO对象,因此也不能将形参或返回类型设置为流类型。
  2. 进行IO操作的函数通常以引用方式传递和返回流。
  3. 读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。
ofstream out1, out2;
out1 = out2; //错误:不能对流对象赋值
ofstream print(ofstream); //错误:不能将形参或是返回类型设为流类型
out2 = print(out2); //错误:out2不是可修改的左值

8.1.2 条件状态

  1. IO类所定义的一些函数和标志,可以帮助我们访问和操纵流的条件状态。
    在这里插入图片描述
  2. 一个流一旦发生错误,其上后续的IO操作都会失败。
  3. 确定一个流对象的状态的最简单的方法是将它当作一个条件来使用。
  4. IO库定义了4个iostate类型的constexpr值表示特定的位模式。
  5. 使用good或fail是确定流的总体状态的正确方法。
  6. iostate类型提供了表达流状态的完整功能,这个类型应作为一个位集合来使用。
auto old_state = cin.rdstate(); //记住cin的当前状态,istream::iostate
cin.clear(); //使cin有效
process_input(cin); //使用cin
cin.setstate(old_state); //将cin置为原有状态

//复位failbit和badbit,保持其他标志位不变
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);

8.1.3 管理输出缓冲

  1. 每个输出流都管理一个缓冲区,用来保存程序读写的数据。
  2. 缓冲刷新:数据真正写到输出设备或文件。
  3. 刷新输出缓冲区:
    flush:刷新缓冲区,但不输出任何额外的字符。
    ends:向缓冲区插入一个空字符,然后刷新缓冲区。
    endl:换行并刷新缓冲区。
    unitbuf:每次输出操作后都刷新缓冲区。
  4. 如果程序崩溃,输出缓冲区不会被刷新。
  5. 有了缓冲机制,操作系统就可以在需要时将程序的多个输出操作合并,提升性能。
//刷新输出缓冲区
cout<< "hi!" <<endl; //输出hi和一个换行,然后刷新缓冲区
cout<< "hi!" <<flush; //输出hi,然后刷新缓冲区,不附加任何额外字符
cout<< "hi!" <<ends; //输出hi和一个空字符,然后刷新缓冲区

//通过设置unitbuf操纵符来控制是否立即刷新
cout<<unitbuf; //所有输出操作后都会立即刷新缓冲区
cout<<nounitbuf; //回到正常的缓冲方式
  1. 交互式系统通常应该关联输入流和输出流。这意味着所有输出,包括用户提示信息,都会在读操作之前被打印出来。
  2. 当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。
  3. 每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream。
cin.tie(&cout); //仅仅是用来展示:标准库内置
//old_tie指向当前关联到cin的流(如果有的话)
ostream *old_tie = cin.tie(nullptr); //cin不再与其他流关联
//将cin与cerr关联;这不是一个好主意,因为cin应该关联到cout
cin.tie(&cerr); //读取cin会刷新cerr而不是cout
cin.tie(old_tie); //重建cin和cout间的正常关联

8.2 文件输入输出

8.2.1 使用文件流对象

  1. 一旦一个文件流已经打开,它就保持与对应文件的关联。
    在这里插入图片描述
ifstream in(ifile); //构造一个ifstream并打开指定文件
ofstream out; //输出文件流,未关联到任何文件
out.open(ifile+".copy"); //打开指定文件
if(out) //检查open是否成功
...;
in.close(); //关闭文件
in.open(ifile+"2"); //打开另一个文件
  1. 当一个fstream对象被销毁时,close会自动被调用。

8.2.2 文件模式

  1. 每个文件流类型都定义了一个默认的文件模式,当未指定文件模式时,就使用此默认模式。
    在这里插入图片描述

  2. 以out(写方式)模式打开文件会丢弃已有数据,保留被ofstream打开的文件中已有数据的唯一方法是显式指定app(追加方式)或in(读方式)模式。

  3. 每次调用open时都会确定文件模式。

  4. 通常情况下,out模式意味着同时使用trunc(截断文件)模式。

//在这几条语句中,file1都被截断
ofstream out("file1"); //隐含以输出模式打开文件并截断文件
ofstream out2("file1", ofstream::out); //隐含地截断文件
ofstream out3("file1", ofstream::out | ofstream::trunc);
//为了保留文件内容,我们必须显示指定app模式
ofstream app("file2", ofstream::app); //隐含为输出模式
ofstream app2("file2", ofstream::out | ofstream::app);

ofstream out; //未指定文件打开模式
out.open("scratchpad"); //模式隐含设置为输出和截断
out.close();
out.open("preious", ofstream::app);//模式为输出和追加
out.close();

8.3 string流

  1. 从string读写数据,就像string是一个IO流一样。
  2. string代表的是内存,istringstrem是从内存读取数据,ostringstream是将数据写入内存。
    在这里插入图片描述

8.3.1 使用istringstream

  1. 当需要处理行内单词的时候,通常可以使用istringstream。
struct PersonInfo{
	string name;
	vector<string> phones;
};

string line, word; //分别保存来自输入的一行和单词
vector<PersonInfo> people; //保存来自输入的所有记录
while(getline(cin,line)){
	PersonInfo info; 		//创建一个保存此记录数据的对象
	istringstream record(line); 	//拷贝刚读入的行
	record >> info.name; 		//读取名字
	while( record >> word) 	//读取电话号码
		info.phones.push_back(word); //保存它们
	people.push_back(info);	 //将此记录追加到people末尾
}

8.3.2 使用ostringstream

for(const auto &entry:people){//对people中每一项
	ostringstream formatted,badNums; //每个循环步创建的对象
	for(const auto &nums:entry.phones){//对每个数
		if(!valid(nums)){
			badNums<<" "<<nums;//将数的字符串形式存入badNums
		}else
			formatted<<" "<<format(nums);//将格式化的字符串“入”formatted
	}
	if(badNums.str().empty()) //没有错误的数
		os<<entry.name<<" "<<formatted.str()<<endl;//打印名字和格式化的数
	else//否则,打印名字和错误的数
		cerr<<"input error:"<<entry.name<<"invalid numbers(s)<<badNums.str()<<endl;
}
  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值