C++----IO流(参考C++ primer)
Posted 4nc414g0n
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++----IO流(参考C++ primer)相关的知识,希望对你有一定的参考价值。
IO流
C++IO库
—截图自cplusplus.com
标准IO流
IO无拷贝和赋值
头文件iostream有三种类:
从流读取数据(istream, wistream)
,向流提取数据(ostream, wostream)
,读写流(iostream, wiosream)
其中开头为w的是其对应的宽字节版本(为了支持宽字节语言)
注意: IO对象无拷贝和赋值
截自c++ primer :
摘自c++ primer
- ------由于不能拷贝IO对象,因此我们也不能将形参或返回类型设置为流类型(参见6.2.1节,第188页)。进行I0操作的函数通常以引用方式传递和返回流。读写一个I0对象会改变.其状态,因此传递和返回的引用不能是const的(
正确用法见下面自定义类重载全局<<
)
IO条件状态及管理 (MARK有问题)
条件状态(截自c++ primer) :
- 在ios_base.h源码中有这样两段:
- 1L<<0为1,1L<<1为2,1L<<2为4,但是根据实际测试与ios_base.h内的不相符,测试结果为:
goodbit=0(正常可用状态)
,badbit=4(出现系统故障的读写错误状态(无法恢复))
,eofbit=1(遇到文件结束符的状态)
,failbit=2(读入错误数据的状态(可恢复))
可以理解为:
例如:
- cin输入与定义的i不对应类型会产生failbit状态
- 测试fail()
- 管理条件状态
- 复位产生新状态
管理输出缓冲区
导致输出缓冲区刷新有很多情况
(-----摘自C++ primer):
- 程序正常结束,作为main函数的return操作的一部分, 缓冲刷新被执行
- 缓冲区满时,需要刷新缓冲,而后新的数据才能继续写入缓冲区,我们可以使用操纵符如endl (参见1.2节,第6页)来显式刷新缓冲区
- 在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区。默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的
- 一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。例如,默认情况下,cin和cerr都关联到cout.因此,读cin或写cerr都会导致cout的缓冲区被刷新
主动刷新输出缓冲区方式:
cout<<"test"<<endl;
输出test和一个换行,然后刷新缓冲区
cout<<"test"<<flush;
输出test,然后刷新缓冲区,不附加任何额外字符
cout<<"test"<<ends;
输出test和一个空字符,然后刷新缓冲区
manipulators
cout<<unitbuf;
: 所有输出操作后都会立即刷新缓冲区
cout << nounitbuf;
: 回到正常的缓冲方式
注意
:如果程序崩溃,输出缓冲区不会被刷新
关联输入输出流:
- 第一种形式返回一个指向绑定输出流的指针
- 第二种形式将对象绑定到 tiestr 并返回指向在调用之前绑定的流的指针(如果有)
标准库默认讲cin和cout关联在一起
:当一个输入流被关联到–个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流(交互式系统通常应该关联输入流和输出流
),如下两者等价
- 解除绑定并保存久绑定:
ostream* old_tie = cin.tie(nullptr);
- 关联新的输入输出流:
cin.tie(old_tie);
重建cin和cout的关联
IO的运算符重载
C++标准库提供了4个全局流对象
cin
、cout
、cerr
、clog
:
- 使用cin进行标准输入即数据通过键盘输入到程序中 (
cin是isream类的对象
)- 使用cout进行标准输出,即数据从内存流向控制台(显示器)
- cerr用来进行标准错误的输出
- clog进行日志的输出
(cout、cerr、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同)
iosfwd文件中:
iostream中:
注意
:
ostream支持参数为streambuf的构造(streambuf有两种:filebuf和stringbuf)
istream同
C++ IO流,使用面向对象+运算符重载的方式
类ostream重载<<
:
- 成员函数
- 非成员函数:
int i = 1; double j = 2.2; cout << i << endl;// 自动识别类型的本质--函数重载 cout << j << endl;// 内置类型可以直接使用--因为库里面ostream类型已经实现了
调用的并不是同一个函数,分别调用的是
ostream& operator<< (int val);
和ostream& operator<< (double val);
,同时之所以可以连续输出是因为有返回值
- 对于内置类型可以自动识别直接使用
- 对于自定义类型,需要全局重载 (类需要加上友元:
friend ostream& operator << (ostream& out, const Date& d);
),以一个成员为内置类型的自定义日期类为例ostream& operator << (ostream& out, const Date& d) out << d._year << " " << d._month << " " << d._day; return out;
类istream重载
>>
:
成员函数:
非成员函数:
自定义类型Date重载
>>
:istream& operator >> (istream& in, Date& d) in >> d._year >> d._month >> d._day; return in;
对于OJ题中的io输入常出现的
while (cin >> str)
如何结束:换行+ctrlZ
while里的cin>>str实际上是operator>>(cin, str)
- 在C++98中istream类里重载了
void*
- C++11中istream类里重载了
bool
:
支持ios及其继承类型的对象
支持显示类型转换成bool
,也就是以为这些类型的对象可以去做条件逻辑判断
以C++11来说也就是:while ((cin >> str).operator bool())
,cin在完成cin>>i之后,将自己转换为了一个bool值,当接收到错误状态,bad、eof、fail在对应错误位置时,cin会在错误状态,就返回false,即循环中止,结束
注意
:这里是显式的类型转换,istream类定义了一个explicit operator bool() 的成员函数。也就是显式的类型转换运算符函数,需要将此类的对象转换为另一种类型时,需要显式调用,如 (bool)cin 或 static_cast(cin),而若这个成员函数不是explicit的,则可以进行隐式转换
,则形如cout<<cin+1;这样的语句就合法了,但是while的条件部分并没有显式转换为什么也合法呢?其实是因为一个例外:即如果表达式被用作条件,则编译器会将显式的类型转换自动应用于它
参考:C++ 类型转换运算符;条件判断部分使用流对象的实质:while(cin>>i)
对于自定义类型Date如果也要这样使用可以
重载 operator bool()
逻辑可以自订(这里是成员_year=0时终止
)
- 在类Date内部加上成员函数operator bool()
operator bool() if (_year == 0) return false; else return true;
想要这样调用while ((cin>>d).operator bool()),需要改库文件里istream里的bool重载,没有意义
文件IO流
C++推荐使用流来读取配置文件
注意:
- 检查if(out):如果open失败,failbit会被置位。如果open失败,条件会为假,我们就不会去使用out了
- 当一个fstream对象离开其作用域时,与之关联的文件会自动关闭
- 默认情况下,当我们打开一个ofstream时,文件的内容会被丢弃。阻止一个ofstream清空给定文件内容的方法是同时指定app模式:
,
ifstream读
注意
:
- 删除了拷贝构造
- 可以创建一个无参文件再使用成员函数open手动打开
- 使用initialization(2)构造,其中的mode有:(多个使用
‘|’
连接)
- 继承了istream的成员函数:
对于内置类型直接使用>>
读出文件内容
ofstream写
ofstream同理:
- 继承ostream的成员函数:
对于内置类型直接<<
写入文件
文件读写&二进制读写
使用文件IO流用文本及二进制方式模拟读写配置文件:
- 文件信息:
struct ServerInfo char _address[32]; //string _address; int _port; ;
- 操作:
struct ConfigManager public: ConfigManager(const char* filename) :_filename(filename) void WriteBin(const ServerInfo& info) ofstream ofs(_filename, ios_base::out | ios_base::binary); ofs.write((const char*)&info, sizeof(info)); void ReadBin(ServerInfo& info) ifstream ifs(_filename, ios_base::in | ios_base::binary); ifs.read((char*)&info, sizeof(info)); void WriteText(const ServerInfo& info) ofstream ofs(_filename); ofs << info._address <<endl<<info._port; void ReadText(ServerInfo& info) ifstream ifs(_filename); ifs >> info._address >> info._port; private: string _filename; // 配置文件 ;
测试
:
- 二进制写和文本写
注意文本写入多个值加上空格换行间隔
对于char[32]类型的_address以二进制方式写入还可以读出,但int类型的port就是未知字符
- 二进制读和文本读
二进制虽然本地打开是乱码,但可以正常读取
文本正常读取
注意
:二进制读写如果将_address改为string类型会出现野指针问题
- 写入的时候是写入的string指针(
string对象实际上是一个指针,内存中是什么样子,就按字节,直接写到文件中读进来也一样,按字节原模原样读进来
),测试sizeof() string对象占40个字节,但string的字符串所占的空间是从堆中动态分配 的,与sizeof()无关- 如果分两个进程来读取,上一次写入的指针,第二个进程来读取这个指针就不一定会指向这个string了
即使是写了之后马上读取也会出错(析构两次)
两个对象(winfo,rbinfo)的string _address地址是一样的,会析构两次,况且,写string并没有把真正的数据写进去
至于文本插入没有问题的原因
:string类重载了流插入(<<)和流提取(>>),遍历string所有元素返回std::ostream& out,也就是字符串本身而不是指针
对于自定义类型Date:
- 前面已经重载了cin,cout,这里ifstream/ofstream切片继承父类istream/ostream, 直接用即可
string流
istringstream
构造:
成员函数:
- str():获取/设置内容(字符串)构造空对象再用str()设置
ostringstream
同理:
使用
有一个学生信息struct
struct Info string _name; int _id; Date _date; string _msg; ;
序列化反序列化:
// 结构信息序列化为字符串 Info winfo = "test", 10, 2022, 9, 4 , "message" ; ostringstream oss; oss << winfo._name << " " << winfo._id << " " << winfo._date << " " << winfo._msg; string str = oss.str(); cout << str << endl; // 获取 Info rInfo; istringstream iss(str); iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg; cout << "name:" << rInfo._name << " (" << rInfo._id << ") "; cout << rInfo._date << endl; cout << rInfo._name << ":>" << rInfo._msg << endl;
其他
加速输入输出
- 标准库默认关联cin和cout底层自动会调用很多flush(同步刷新缓冲区)
- C++为了兼容C,sync_with_stdio(输入同步开关)。如果流是同步的,则程序可以将 iostream 操作与 stdio 操作混合,并保证它们的可观察效果遵循与线程中使用的相同顺序
关闭提升速度:std::cin.tie(nullptr) ,std::ios::sync_with_stdio(false);
注意
关闭同步后不要混用scanf ,getchar,gets,fgets,fscanf和cin
IO库再探(C++ Primer-17.5)
格式化输入输出
非格式化输入输出
流随机访问
以上是关于C++----IO流(参考C++ primer)的主要内容,如果未能解决你的问题,请参考以下文章
C++ Primer 5th笔记(chap 17 标准库特殊设施)未格式化的输入/输出操作