说明
C/C++语言不直接处理输入输出,而是通过标准库中的类型来处理IO。
类型 ifstream
和 istringstream
都继承自 istream
。
类型 ofstream
和 ostringstream
都继承自 ostream
。
可以将一个派生类(子类)对象当作基类(父类)对象来使用。
iostream
iostream 定义了用于读写流的基本类型。
头文件:#include<isotream>
-
istream
:输入流类型,提供输入操作。 -
ostream
:输出流类型,提供输出操作。
iostream:
-
cin:istream
类对象,从标准输入读取数据,默认情况下关联到用户的控制台窗口。 -
cout:ostream
类对象,向标准输出写入数据,默认情况下关联到用户的控制台窗口。 -
cerr:ostream
类对象,通常用于输出程序错误消息,写入到标准错误,默认情况下关联到用户的控制台窗口。 -
clog:ostream
类对象,通常用于输出程序运行时一般信息(运行日志),默认情况下关联到用户的控制台窗口。
宽字符版本的类型和函数的名字以一个w开始:wcin
、wcout
和 wcerr
是分别对应 cin
、cout
和 cerr
的宽字符版对象。
>>
运算符:从一个 istream
对象读取输入数据。<<
运算符:向一个 ostream
对象写入输出数据。getline
:从给定的 istream
读取一行数据,存入给定的 string 对象中。
代码演示:IO对象无赋值,无拷贝。
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream out1, out2;
out1 = out2; //不允许对流对象赋值
ofstream print(ofstream); //C++PrimerPlus中源码注释,不能初始化ofstream参数,但是vs2022编译器未报错。todo
out2 = print(out1); //不能拷贝流对象
return 0;
}
不能将形参或返回类型设置为流类型。
进行IO操作的函数通常以引用方式传递和返回流。
读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。
IO库操作过程中的条件状态:
流发生错误,其上后续的IO操作都会失败。
只有当流为无错状态时,才可以通过流读写数据。
代码演示:确定流状态的方式:作为判断条件。
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
int num = 1;
if (cin >> num)
{
cout << "stream success" << endl;
cout << num << endl;
}
return 0;
}
运行结果:
IO库定义了一个与机器无关的iostate类型,它提供了表达流状态的完整功能。
badbit
表示系统级错误,如不可恢复的读写错误。
在发生可恢复错误后,failbit
被置位,如期望读取数值却读出一个字符等错误。这种问题通常是可以修正的,流还可以继续使用。
如果到达文件结束位置,eofbit
和 failbit
都会被置位。
goodbit
的值为 0
,表示流未发生错误。
如果badbit
、failbit
和 eofbit
任一个被置位,则检测流状态的条件会失败。
标准库定义查询标志位状态的函数:
-
good
:所有错误位均未置位的情况下返回true
。 -
bad
,fail
,eof
则对应错误位被置位时返回true
。 -
badbit
被置位时,failed返回true
。 -
good
和fail
是确定流总体状态的正确方法,将流当作条件使用的代码等价于!fail
。
流对象的 rdstate
成员返回一个 iostate
值,对应流的当前状态。setstate
操作将给定条件位置位,表示发生了对应错误。clear
不接受参数的版本清除(复位)所有错误标志位。执行 clear()
后,调用 good
会返回 true
。
带参数的 clear
版本接受一个 iostate
值,表示流的新状态。
代码演示:操作流状态。
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
int data;
auto old_state = cin.rdstate(); //记住当前状态
cin.clear(); //使cin有效
cin >> data; //使用cin
cin.setstate(old_state); //将cin置为原有 old_state 状态
//用rdstate读出当前条件状态,将failbit和badbit复位,其他条件状态保持不变。
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);
return 0;
}
刷新缓冲区:
- 程序正常结束,作为 main 函数返回的一部分,缓冲刷新被执行。
- 缓冲区满。
- 使用操纵符如endl。
- 在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区。
- 默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的。
- 一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。
- 默认情况下,cin和cerr都关联到cout。因此,读cin或写cerr都会导致cout的缓冲区被刷新。
代码演示:刷新输出缓冲区
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
cout << "hello, " << endl; //换行并刷新缓冲区
cout << "world!!!" << flush; //刷新缓冲区
cout << "C++" << ends <<"***"; //向缓冲区加空字符刷新缓冲区,vs2022代码实测无空白字符 todo
cout << unitbuf; //所有输出操作后立即刷新缓冲区
//所有输出操作后立即刷新缓冲区
//所有输出操作后立即刷新缓冲区
//所有输出操作后立即刷新缓冲区
cout << nounitbuf; //回到正常系统管理的缓冲区刷新机制
return 0;
}
运行结果:
警告:程序崩溃,输出缓冲区不会被刷新。
代码演示:cin 对象 tie 成员函数使用及说明。
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
//tie 第一个版本不带参数,返回指向输出流的指针
ostream* old_tie_1 = cin.tie(); //old_tie_1 指向 cout对象,标准库已经将 cin 和 cout 关联.
if (old_tie_1)
{
*old_tie_1 << "hello world" << endl;
}
else
{
cout << "nummptr == old_tie_1" << endl;
}
//如果对象未关联到流,返回空指针
//tie的第二个版本接受一个指向ostream的指针,将自己关联到此ostream。
cin.tie(&cout); //将 cin 关联到 cout。标准库已经将 cin 和 cout 关联。
ostream* old_tie_2 = cin.tie(nullptr); //cin 不再与其他流关联。
ostream* old_tie_3 = cin.tie(); //old_tie_3 指向 cout对象。
if (old_tie_3)
{
*old_tie_3 << "hello world" << endl;
}
else
{
cout << "nummptr == old_tie_3" << endl;
}
cin.tie(&cout); //将 cin 关联到 cout。
ostream* old_tie_4 = cin.tie(); //old_tie_4 指向 cout对象
if (old_tie_4)
{
*old_tie_4 << "hello world" << endl;
}
else
{
cout << "nummptr == old_tie_4" << endl;
}
return 0;
}
运行结果:
每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream。
fstream
fstream定义了读写命名文件的类型。
头文件:#include<fstream>
fstream:
-
istream
:从文件读取数据。 -
ostream
:向文件写入数据。 -
fstream
:读写文件。
宽字符版本的类型和函数的名字以一个w开始:wistream
、wostream
和 wfstream
是分别对应 istream
、ostream
和 fstream
的宽字符版。
除了继承自 iostream 外,fstream新增成员管理与流关联的文件:
当一个 fstream
对象被销毁时, close
会自动被调用。
每个流都有一个关联的文件模式,文件模式指出如何使用文件:
指定文件模式限制如下:
- 只可以对 ofstream 或 fstream 对象设定 out 模式。
- 只可以对 ifstream 或 fstream 对象设定 in 模式。
- 只有当 out 设定时,才可以设定 trunc 模式。
- 只要 trunc 模式没被设定,就可以设定 app 模式。
- app 模式下,即使没有显式指定 out 模式,文件也总是以输出方式打开。
- 默认情况下,没有指定 trunc 模式,out 模式打开的文件也会被截断。
- 为了保留以 out 模式打开的文件的内容,必须同时指定 app 或者 in。
- ate和binary模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用。
在每次打开文件时,都要设置文件模式,可能是显式地设置,也可能是隐式地设置。
当程序未指定模式时,就使用默认文件模式:
- ifstream :默认 in 模式。
- ofstream:默认 out 模式(打开文件会都是已有数据)。
- fstream:默认 int 和 out 模式。
对于一个给定流,每当打开文件时,都可以改变其文件模式。
sstream
sstream定义了读写内存string对象的类型。
头文件:#include<sstream>
fstream:
-
istringstream
:从string
读取数据。 -
ostringstream
:向string
写入数据。 -
stringstream
:读写string
。
宽字符版本的类型和函数的名字以一个w开始:wistringstream
、wostringstream
和 wstringstream
是分别对应 istringstream
、ostringstream
和 stringstream
的宽字符版。
stringstream 新增管理与流相关联的string:
代码演示:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
//istringstream:当我们的某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用istringstream。
//假定有一个文件,列出了一些人和他们的电话号码。
//某些人只有一个号码,而另一些人则有多个——家庭电话、工作电话、移动电话等。
//我们的输入文件看起来可能是这样的:
//morgan 2015552368 8625550123
//drew 9735550130
//lee 6095550132 2015550175 8005550000
//记录姓名和电话信息
//ostringstream:当我们逐步构造输出,希望最后一起打印时,ostringstream是很有用的。
//想逐个验证电话号码并改变其格式。如果所有号码都是有效的,我们希望输出一个新的文件,包含改变格式后的号码。
//对于那些无效的号码,我们不会将它们输出到新文件中,而是打印一条包含人名和无效号码的错误信息。
//由于我们不希望输出有无效电话号码的人,因此对每个人,直到验证完所有电话号码后才可以进行输出操作。
struct PersionInfo
{
string name;
vector<string> phone;
};
int main()
{
string line, word;
vector<PersionInfo> people;
while (getline(cin, line))
{
PersionInfo info;
istringstream record(line); //将记录绑定到读入的行
record >> info.name; //读取名字
while (record >> word) //读取电话
{
info.phone.push_back(word);
}
people.push_back(info);
}
for (auto& itr : people)
{
cout << "name:" << itr.name << "--";
for (auto& iphone_itr : itr.phone)
{
cout << iphone_itr << ",";
}
cout << " ." << endl;
}
for (const auto& entry : people)
{
ostringstream formatted, badNums;
for (const auto& nums : entry.phone)
{
if (nums.size() > 12)
{
badNums << " " << nums;
}
else
{
//输出拼接
formatted << entry.name << " phone: "<<nums << "is success" << endl;
cout << formatted.str() << endl;
}
}
}
return 0;
}
运行结果: