8.1 IO类
8.2 文件输入输出
8.3 string流
iostream头文件
定义了用于读写流的基本类型fstream头文件
定义了读写命名文件的类型sstream头文件
定义了读写内存string对象的类型
- 为了支持宽字符的语言,标准库定义了一组类型和对象来操纵wchar_t类型的数据.这种类型的名字以w开始.
IO类型间的关系
- 通过继承机制,标准库使我们可以忽略这些不同类型之间的流之间的差异
8.1.1 IO对象无拷贝或赋值
8.1.2 条件状态
- IO操作一个与生俱来的问题是可能发生错误.下标列出了IO类所定义的一些函数和标志,可以帮助我们访问和操作流的条件状态.
查询流的状态
管理条件状态
8.1.2练习题
istream & test(istream & is)
{
int v;
while(is>>v,!is.eof()){ //遇到文件结束符终止
if(is.bad()) //若流的badbit置位,则返回true
throw runtime_error("IO流错误");
if(is.fail()){//若流的badbit或者failbit位置位,则返回true
cerr<<"数据错误,请重载: "<<endl;
is.clear(); //将流的所有条件状态位复位
is.ignore(100,'\n');
continue;
}
cout<<v<<endl;
}
is.clear();
return is;
}
int main()
{
cout<<"请输入一些整数,按Ctrl+Z结束"<<endl;
test(cin);
return 0;
}
- C++中的输入输出系统负责记录每一个输入输出操作的结果信息,这些当前的状态信息被包含在io_state类型的对象中。io_state是一个枚举类型,以下便是它包含的值。
eofbit
已到达文件尾failbit
非致命的输入/输出错误,可挽回badbit
致命的输入/输出错误,无法挽回
这三个标志位均用一位二进制位来表示,0表示清除,1表示设置。对于eofbit,failbit,badbit。0表示正常,1表示被设置。- 这三个整体构成了流的状态。若三个均为0,表示流状态正常,反之,若有一个为1,流状态非正常。包括达到了文件尾,出现可恢复性错误,出现了不可恢复性的错误。
- 另外还有一个标志位goodbit用来测试流状态是否正常。如果流状态正常返回true,输出1.否则输出0。
- 相对应与这四个标志位有四个函数分别用来测试相应的标志位的状态。他们分别为:
bool bad();
bool eof();
bool fail();
bool good();
看上去这四个函数的返回值是bool类型,返回值应该是true和false,也就是在我们C++里,0代表false,非0代表true,但是,在我们输出他们的结果时,得到的是0和1.不是true和false,也不是0和非0值。
原文链接:https://blog.csdn.net/wangfutao01/article/details/6469734
对于流状态的测试我们有三种方法。
方法一:
#include <iostream>
using namespace std;
int main()
{
cout << "hello" << endl;
cout << cout.rdstate() << endl;//输出当前的流状态:0
if(cout.rdstate() == ios::goodbit)
{
cout<<"输出数据的类型正确,无错误!"<<endl; //输出
}
if(cout.rdstate() == ios_base::failbit)
{
cout<<"输出数据类型错误,非致命错误,可清除输入缓冲区挽回!"<<endl;
}
system("pause");
return 0;
}
//由此可知,rdstate() 返回的是eofbit,failbit,badbit三个标志的值,即返回的是一个strm::iostate类型。
方法二:我们可以用三个标志位对应的函数来测试当前的流状态。
#include <iostream>
using namespace std;
int main()
{
cout << "你好吗?" << endl;
cout << cout.eof() << cout.fail() << cout.bad() << endl;
cout << cout.good() << endl;
return 0;
/*
* 你好吗?
000
1
*/
}
//我们可以用三个标志位对应的函数来测试当前的流状态。
方法三:用流本身来测试
如:
if (cin)
while (cin >> word)
if语句直接检查流的状态,while语句则检测表达式的返回值,即间接的检查流状态。
include <iostream>
using namespace std;
int main()
{
int i;
cin >> i;
if (cin)
{
cout << "流状态正常" << i << endl;
}
else
{
cout << "流状态不正常" << i << endl;
}
return 0;
}
8.1.3 管理输出缓冲
导致缓冲刷新:
- 程序正常结束
- 缓冲区已满
- 使用操作符
endl
来显式刷新缓冲区 - 一个流可能关联到另外一个流.例如:cin和cerr都关联到cout.因此读cin或写cerr都会导致cout的缓冲区被刷新.
- 可以使用unitbuf设置流的内部状态.
刷新输出缓冲区
cout<<"hi"<<endl;
cout<<"hi"<<endl;
cout<<"hi"<<ends;//输出hi和一个空字符,然后刷新缓冲区
unitbuf操作符
- unitbuf 操作符.如果想在每次输出操作后都刷新缓冲区,我们可以使用 unitbuf 操作符,它告诉流在接下来的每次写操作之后都进行一次 flush 操作。而 nounitbuf 操作符则重置流, 使其恢复使用正常的系统管理的缓冲区刷新机制):
cout << unitbuf; //所有输出操作后都会立即刷新缓冲区
//任何输出都立即刷新,无缓冲
cout << nounitbuf; //回到正常的缓冲方式
如果程序崩溃,输出缓冲区不会被刷新.当调试一个已经崩溃的程序时,需要确定那些你以为已经输出的数据确实已经刷新了
关联输入和输出流
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。标准库将 cout 和 cin 关联在一起,因此下面语句:
cin >> ival;
导致 cout 的缓冲区被刷新。
//tie()不带参数的版本:返回指向输出流的指针(仅当本对象关联到一个输出流上时,未关联则返回NULL)
cout << cin.tie()<<endl;
//0x6fd5acc0
cout<<(&cout)<<endl;
//0x6fd5acc0
//tie()带参数的版本接受一个指向ostream的指针,将自己关联到此ostream上
ostream *old_tie = cin.tie(nullptr);
cout<<cin.tie()<<endl; //0
cin.tie(&cerr); //将自己关联到cerr,读取cin会刷新cerr而不是cout
cout<<cin.tie()<<ends; //0x6fd5aa80
cin.tie(old_tie); //重建cin和cout间的正常关联
- 每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream.
8.2 文件输入输出
头文件fstream定义了三个类型来支持文件IO
ifstream
从一个给定文件读取数据.ofstream
向一个给定文件写入数据fstream
可以读写给定文件
特别的,我们可以用IO运算符(<<或>>)来读写文件,可以用getline从一个ifstream读取数据
- 除了继承iostream类型的行为之外,fstream中定义的类型还增加了一些新的.
8.2.1 使用文件流对象
- 每个文件流类都定义了一个名为open的成员函数,它完成一些系统相关的操作,来定位给定的文件,并视情况打开为读或写模式
ifstream in(file);
ofstream out;//输出文件流未关联到任何文件
创建文件流对象时,当提供一个文件名时,则open会自动被调用.\
fstream代替iostream&
- 接受一个iostream的地方可以使用fstream来替代
成员函数open和close
ofstream out;
out.open(s);
- 如果调用open失败,failbit会被置位;
- 实际上,对一个已经打开的文件流调用open会失败,并会导致failbit被置位.为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件.
in.close()
in.open()
自动构造和析构
当一个fstream对象离开其作用域,与之关联的文件会自动关闭,close会自动被调用
- 一次读取一行
int main()
{
ifstream in("a.txt");
if(!in){
cerr<<"无法打开文件"<<endl;
return -1;
}
string line;
vector<string> words;
while(getline(in,line)){
words.push_back(line);
cout<<" --- "<< line<<ends;
}
in.close();
vector<string>::const_iterator it = words.begin();
while(it != words.end()){
cout<<*it<<endl;
++it;
}
return 0;
}
- 一次读取一个字符
while(in>>line){ //遇见空格停止
words.push_back(line);
cout<<"++"<< line<<ends;
}
8.2.2 文件模式
- 每个流都有一个关联的文件模式,用来指出如何使用文件
以out模式打开文件会丢失已有数据
每次调用open时都会确定文件模式
8.3 string流
8.3.1 使用istringstream
LiLi 1212121212 3434343434
xiaomi 45454545455 6767676767
using namespace std;
struct Person{
string name;
vector<string> phonenumber;
};
int main()
{
ifstream in("E:\\practise\\Clion\\untitled\\a.txt");
vector<Person> personInfo;
if(!in){
cerr<<"无法打开文件"<<endl;
return -1;
}
string line,phone;
vector<string> words;
while(getline(in,line)){ //遇见空格停止
Person person;
istringstream recordLine(line);
recordLine >> person.name;
while(recordLine>>phone)
person.phonenumber.push_back(phone);
personInfo.push_back(person);
}
vector<Person>::const_iterator its = personInfo.begin();
while(its != personInfo.end()){
cout<<its->name<<ends;
vector<string>::const_iterator it = its->phonenumber.begin();
while(it != its->phonenumber.end()){
cout<<*it<<ends;
it++;
}
cout<<endl;
++its;
}
return 0;
}
我们将一个istringstream与刚刚读取的文本行进行绑定,这样就可以课istringstream上使用输入运算符来读取当前记录中的每个元素.