IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 第八章 IO库 -> 正文阅读

[C++知识库]第八章 IO库

目录

8.1 IO类

8.1.1 IO对象无拷贝或赋值

8.1.2条件状态

8.1.3管理输出缓冲

8.2 文件输入输出

8.2.1 使用文件流对象

?8.2.2文件模式

8.3 string流

8.3.1 使用istringstream

8.3.2 使用ostringstream

小结

?术语表


已知的几种IO库设施:istream,ostream,cin,cout,cerr,>>,<<,getline

8.1 IO类

为了支持使用宽字符的语言,标准库定义了一组类型和对象来操纵wchar_t类型的数据。宽字符版本的类型和函数的名字以一个w开始。列如wcin,wout,werr。 宽字符版本的类型和对象与其对应的普通 char 版本的类型定义在同一个头文件中。例如,头文件fstream定义了ifstream 和 wifstream类型。

IO类型间的关系->继承机制

8.1.1 IO对象无拷贝或赋值

ofstream out1, out2;
out1 = out2;//错误,不能对流对象赋值
ofstream print(ofstream); //错误:不能初始化ofstream参数
out2 = print(out2);  //错误:不能拷贝流对象

1.由于不能搓贝IO对象,因此我们也不能将形参或返回类型设置为流类型

2.传递和返回的引用不能是const(进行IO操作的函数通常以引用方式传毒和返回流。读写一个IO对象会改变其状态)

8.1.2条件状态

?while循环检查>>表达式返回的流的状态。如果输入操作成功,流保持有效状态,则条件为真

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

查询流的状态

iostate类型:提供表达流的完整功能,此类型应该作为一个位集合来使用,使用方法与quizl相同

4个iostate类型的constexpr值:表示特定的位模式,表示特定类型的IO条件,可与位运算符一起使用来一次性检测或设置多个标志位

badbit:badbit表示系统级错误,如不可恢复的读写错误。通常情况下,一旦 badbit被置位,流就无法再使用了。在发生可恢复错误后,failbit被置位,如期望读取数值却读出一个字符等错误。这种问题通常是可以修正的,流还可以继续使用。如果到达文件结束位置,eofbit和 failbit 都会被置位。goodbit的值为0,表示流未发生错误。如果badbit、failbit和 eofbit任一个被置位,则检测流状态的条件会失败。

标准库还定义了一组函数来查询这些标志位的状态

管理条件状态

流对象的rdstate成员返回一个iostate值,对应流的当前状态

setstate操作:将给定条件条件位置位,表示发生了对应错误

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

clear成员:重载成员,有一个不接受参数的版本,可清楚(复位)所有错误标志位,执行clear()后调用good会返回true;而另一个版本接受一个iostate类型的参数

//记住cin的当前状态
auto old_state = cin.rdstate();//记住cin的当前状态
cin.clear();//使cin有效
process_input(cin); //使用cin
cin.setstate(old_state);//将cin置为原有状态

带参数的clear版本接受一个iostate值,表示流的新状态。为了复位单一的条件状态位,我们首先用rdstate读出当前条件状态,然后用位操作将所需位复位来生成新的状态。例如,下面的代码将failbit和 badbit复位,但保持eofbit不变:

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

8.1.3管理输出缓冲

导致缓冲刷新(即,数据真正写到输出设备或文件)的原因有很多:
1·程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行。
2缓冲区满时,需要刷新缓冲,而后新的数据才能继续写入缓冲区。
3我们可以使用操纵符如 endl(参见1.2节,第6页)来显式刷新缓冲区。
4在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区。默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的。
5一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。例如,默认情况下,cin和cerr都关联到cout。因此,读cin或写cerr都会导致cout的缓冲区被刷新。
?

?刷新输出缓缓冲区

cout << "hi!" << endl;//输出hi和一个换行,然后刷新缓冲区
cout << "hi!" << flush;//输出hi,然会刷新缓冲区,不附加任何额外字符
cout << "hi!" << ends;//输出hi和一个空字符,然后刷新缓冲区

unitbuf操纵符

如果想在每次输出操作后都刷新缓冲区,我们可以使用unitbuf操纵符。它告诉流在接下来的每次写操作之后都进行一次flush 操作。而nounitbuf操纵符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制: 如果想在每次输出操作后都刷新缓冲区,我们可以使用unitbuf操纵符.它告诉流在接下来的每次写操作之后都进行一次操作.而nounitbuf操纵符则重置流,使其恢复使用正常的系统管理的缓冲区刷新机制:

cout << unitbuf; Cout<<单位; //所有输出操作后都会立即刷新缓冲区
//任何输出都立即刷新,无缓冲  
cout << nounitbuf; Cout<<nounitbuf;
//回到正常的缓冲方式 //回到正常的缓冲方式


?

?关联输入和输出流

cin >> ival; //导致cout的缓冲区被刷新

?两个tite重载的版本:不带参数,返回指向输出流的指针。or接受一个指向ostream的指针,将自己关联到此ostream,即x.tie(&o)将流x关联到输出流o

cin.tie(&cout); //仅仅是用来展示:标准库将cin和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间的正常关联

?在这段代码中,为了将一个给定的流关联到一个新的输出流,我们将新流的指针传递给了tie。为了彻底解开流的关联,我们传递了一个空指针。每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream。 在这段代码中,为了将一个给定的流关联到一个新的输出流,我们将新流的指针传递给了领带。为了彻底解开流的关联,我们传递了一个空指针.每个流同时最多关联到一个流,但多个流可以同时关联到同一个Ostream.

8.2 文件输入输出

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

ifstream从一个给定文件读取数据

ofstream向一个给定文件写入数据

fstream可以读写给定文件

可以对fstream,ifstream,ofstream对象调用一下操作,但不能对其他IO类型调用这些操纵

?

8.2.1 使用文件流对象

创建文件流对象时,我们可以提供文件名(可选的)。如果提供了一个文件名,则open会自动调用

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

这段代码定义了一个输入流in,它被初始化为从文件读取数据,文件名由 string类型的参数ifile指定。第二条语句定义了一个输出流 out,未与任何文件关联。在新C++标准中,文件名既可以是库类型string对象,也可以是C风格字符数组(参见3.5.4节,第109页)。旧版本的标准库只允许C风格字符数组。

用fstream代替iostream&

我们在8.1节(第279页)已经提到过,在要求使用基类型对象的地方,我们可以用继承类型的对象来替代。这意味着,接受一个iostream类型引用(或指针)参数的函数,可以用一个对应fstream(或sstream)类型来调用。也就是说,如果有一个函数接受一个 ostream&参数,我们在调用这个函数时,可以传递给它一个ofstream对象,对istream&和 ifstream也是类似的。??
?

用7.13节中的read和print函数来读写命名文件。本例中假定输入和输出文件的名字是通过传递给main函数的参数来指定的


ifstream input(argv[1]);  //打开销售记录文件
ofstream output(argv[2]); //打开输出文件
Sales_data total;         //保存销售总额的变量
if (read(input, total))   //读取第一条销售记录
{ 
	Sales_data trans;     //保存下一条销售记录的变量
	while (read(input, trans)) //读取剩余记录
	{
		if (total.isbin() == trans.isbn())  //检查isbn
			total.combine(trans);           //更新销售总额
		else {
			print(output, total) << endl;   //打印结果
			total = trans;                  //处理下一本书
		}
	}
	print(output, total) << endl;           //打印最后一本数的销售额
}
else
cerr << "No data?!" << endl;

成员函数open和close

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

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

调用open失败,failbit会被置为,因为调用open可能失败,进行open能否成功的检测通常是一个好习惯

if (out)    //检查open能否成功
            //open成功,就可以使用文件了

一旦一个文件流已经打开,它就保持与对应文件的关联。实际上,对一个已经打开的文件流调用open会失败,并会导致failbit被置位。随后的试图使用文件流的操作都会失败。为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件。一旦文件成功关闭.我们可以打开新的文件,

in.close();    //关闭文件
in.open(ifile + "2");  //打开另一个文件

?如果open成功,则open会设置流的状态,使得good()为true

自动构造和析构

考虑这样一个程序,其main函数接受一个要处理的文件列表,这种程序可能会有如下的循环

//对每个传递给程序的文件执行循环操作
for (auto p = argv + 1; p != argv + argc; ++p)
{
	ifstream input(*p); //创建输出流并打开文件
	if (intput) {       //如果文件打开成功,”处理“此文件
		process(input);
	}
	else
	{
		cerr << "couldnt't open:" + string(*p);
	}
}//每个循环步input都会离开作用域,因此会被销毁

每个循环步构造一个新的名为input的ifstream对象,并打开它来读取给定的文件。像之前一样,我们检查open是否成功。如果成功,将文件传递给一个函数,该函数负责读取并处理输入数据。如果 open 失败,打印一条错误信息并继续处理下一个文件。
? ? ? ?因为input是while循环的局部变量,它在每个循环步中都要创建和销毁一次(参见5.4.1节,第165页)。当一个fstream对象离开其作用域时,与之关联的文件会自动关闭。在下一步循环中,input会再次被创建。

当一个fastream对象被销毁时,close会被自动调用

?8.2.2文件模式

每个流都有一个关联的文件模式,用来指出如何使用文件

?无论那种方式,都可以指定文件模式。指定文件模式有如下限制

?每个文件流类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式。

以out模式打开文件会丢弃已有的数据

阻止一个ofstream清空给定文件内容的方法是同时指定app模式:

//在这几条语句中,filel都被截断
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);


?每次调用open时都会确定文件模式


ofstream out; //未指定文件打开模式
out.open("scratchpad"); //模式隐含设置为输出和截断
out.close();//关闭out,以便我们将其用于其他文件
out.open("Precious", ofstream::app);//模式为输出和追加
out.close();

第一个open调用未显式指定输出模式,文件隐式地以out模式打开。通常情况下,out模式意味着同时使用trunc模式。因此,当前目录下名为scratchpad的文件的内容将被清空。当打开名为precious 的文件时,我们指定了append模式。文件中已有的数据都得以保留,所有写操作都在文件末尾进行。

?

8.3 string流

sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,从string读取数据,就像是string是一个IO流一样

istringstream从string读取数据,ostringstream向string写入数据,头文件stringstream既可以从string读数据也可向string写数据。

与fstream类型类似,头文件sstream中定义的类型都继承我们已经使用过的iostream头文件中定义的类型,处理继承得来得操作,sstream中定义得类型还增加了一些成员来管理与流相关联得string

?上述操作可以对stringstream对象调用这些操作。但不能对其他IO类型调用这些操作

8.3.1 使用istringstream

某些工作是对整行文本进行处理,而其他一些工作是处理行内得单个单词时,通常可以使用istringstream

?首先定义一个简单得类来描述输入数据

//成员默认为公有
struct PersonInfo {
	string name;
	vector<string>phones;
};

程序读取数据文件,创建一个PersonInfo得vector。vector中每个元素对应文件中的一条记录。在一个循环中处理输入元素,每个循环步读取一条记录,提取出一个人名和若干个电话号码

string line, word;//分别保存来自输入的一行和单词
vector<PersonInfo>people;  //保存来自输入的所有记录
//运行从输入读取数据,直至cin遇到文件尾(或其他错误)
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末尾
}

这里我们用getline 从标准输入读取整条记录。如果getline 调用成功,那么line中将保存着从输入文件而来的一条记录。在while 中,我们定义了一个局部PersonInfo对象,来保存当前记录中的数据。

接下来我们将一个 istringstream与刚刚读取的文本行进行绑定,这样就可以在此 istringstream 上使用输入运算符来读取当前记录中的每个元素。我们首先读取人名,随后用一个while循环读取此人的电话号码。

当读取完line中所有数据后,内层 while循环就结束了。此循环的工作方式与前面章节中读取 cin 的循环很相似,不同之处是,此循环从一个string 而不是标准输入读取数据。当string中的数据全部读出后,同样会触发“文件结束”信号,在record上的下一个输入操作会失败。

我们将刚刚处理好的 PersonInfo追加到vector中,外层while循环的一个循环步就随之结束了。外层while循环会持续执行,直至遇到cin 的文件结束标识。
?

8.3.2 使用ostringstream

由于我们不希望输出有无效电话号码的人,因此对每个人,直到验证完所有电话号码后才可以进行输出操作。但是,我们可以先将输出内容“写入”到一个内存ostrinastream 中:
?

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;
}

在此程序中,我们假定已有两个函数,valid和format,分别完成电话号码验证和改变格式的功能。程序最有趣的部分是对字符串流formatted和 badNums 的使用。我们使用标准的输出运算符(<<)向这些对象写入数据,但这些“写入”操作实际上转换为string操作,分别向formatted和l badNums中的string对象添加字符。

小结

?术语表

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-07 11:00:54  更:2022-05-07 11:02:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 2:11:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码