本篇随笔为转载,原贴地址:《C++ Primer》第8章 IO库 学习笔记。
1.IO类
1 #include <iostream> 2 istream//从流中读取数据 3 ostream//从流中写入数据 4 iostream//读写流 5 6 #include <fstream>//文件 7 ifstream 8 ofstream 9 fstream 10 11 #include <sstream>//字符串 12 istringstream 13 ostringstream 14 iostringstream
fstream和sstream都继承于iostream,所以iostream有的操作他们都有。
另外流前面加字母w表示针对宽字符。
2.IO对象无拷贝和赋值
1 ostream o1,o2; 2 o1 = o2; 3 ostream o3(o1);
错误,不能拷贝和赋值
因为流不能拷贝和赋值,所以流作为函数参数或者是返回值必须是引用类型,且读写一个IO对象会改变其状态,
因此传递和返回的引用不能是const的
就好像
const成员函数不可改变对象内容,同时也不可通过函数返回值改变对象内容。 所以返回指向对象的指针
或引用都会被编译器拦住的。 const指针或const引用不会改变对象内容,所以可以返回。
3.IO条件状态
1 strm代表一种IO类型,流处于好的状态时这些bit位全部处于0。 2 3 strm::iostate iostate一种机器相关类型,提供了表达条件状态的完整功能 4 strm::badbit 指出流已经崩溃 5 strm::failbit 指出IO操作失败 6 strm::eofbit 指出流达到了文件的末尾 7 strm::goodbit 指出流处于未错误状态
1 s.eof() 若流处于eofbit,返回true 2 s.fail() 若流处于failbit,返回true 3 s.bad() 若流处于badbit,返回true 4 s.good() 若流处于有效状态,返回true 5 s.clear() 将流所有状态恢复,设置为有效,返回void 6 s.clear(flag) 将指定bit位恢复,设置为有效,返回void。flag类型为strm::iostate 7 s.setstate(flag) 根据给定的bit标志位,将流s中对应条件状态位置位。flag类型为strm::iostate 8 s.rdstate() 读取当前的状态
例子:
1 #include <iostream> 2 #include <fstream> 3 4 using namespace std; 5 6 istream& read(istream& is) 7 { 8 int i; 9 while(is >> i && i != EOF) 10 { 11 cout << i << endl; 12 } 13 cout << "eof:" << is.eof() << endl; 14 cout << "fail:" << is.fail() << endl; 15 cout << "bad:" << is.bad() << endl; 16 cout << "good:" << is.good() << endl; 17 cout << "rdstate " << is.rdstate() << endl; 18 19 cout << endl; 20 21 //is.clear();和下面等价 22 is.clear(is.rdstate() & ~is.failbit & ~is.eofbit); 23 //is.setstate(is.eofbit);设置了后,rdstate()结果是2 24 25 cout << "eof:" << is.eof() << endl; 26 cout << "fail:" << is.fail() << endl; 27 cout << "bad:" << is.bad() << endl; 28 cout << "good:" << is.good() << endl; 29 30 cout << endl; 31 cout << "rdstate " << is.rdstate() << endl; 32 33 return is; 34 } 35 36 int main() 37 { 38 read(cin); 39 40 return 0; 41 }
结果,上面是未注释掉is.setstate(is.eofbit);的结果
下面是注释掉的结果:
还有一种可以保存流当前状态,清空,然后再恢复。
1 auto old_state = is.rdstate( ); 2 is.clear( ); 3 is.setstate(old_state);
4.管理缓冲区
1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 cout << "hi!" << endl; 7 cout << "hi!" << ends; //缓冲区插入一个字符,然后刷新 8 cout << "hi!" << flush; //刷新缓冲区,不输出任何字符 9 }
5.关联输入输出缓冲区
任何试图从输入流读数据的操作都会先刷新关联的输出流。
1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 int i; 7 cin >> i; //刷新cout 和 cerr; 8 ostream *old_str; 9 old_str = cin.tie(nullptr); //解开流,传递一个空指针。返回旧解 10 ostream &os = cout; //新建一个流 11 cin.tie(&os); //绑定这个新流 12 cin >> i; 13 cin.tie(old_str); //恢复以前的状态 14 }
6.文件的输入和输出
头文件定义了三种IO
ifstream从文件中读取数据
ofstream向一个文件写数据
fstream可以读写
1 //open:流用open和文件关联起来,如果open失败,failbit会被置位。因为open可能会失败,所以检查流是个好习惯 2 //close:当将流文件关联到另外一个文件时,必须用close关闭当前的文件。 3 4 #include <iostream> 5 #include <fstream> 6 #include <string> 7 8 using namespace std; 9 10 std::istream& read(std::istream& is, std::string& s) //不写引用就成为了副本 11 { 12 is >> s; 13 return is; 14 } 15 16 std::ostream& print(std::ostream& os, std::string& s)//可以向iostream传递fstream,因为是派生类,不过对文件最好使用fstream 17 { 18 os << s; 19 return os; 20 } 21 22 23 int main(int argc, char *argv[]) 24 { 25 std::ifstream input(argv[1]); 26 std::ofstream output(argv[2]); 27 28 std::string s; 29 read(input, s); 30 std::cout << s << std::endl; 31 print(output, s); 32 std::string ss; 33 //std::getline(ss, argv[1]); 读取一行 34 std::cin >> ss; 35 36 std::string ifile = "file2"; 37 std::ifstream in(ifile); //新建一个文件流,并且初始化,初始化意味着open() 38 std::ofstream out; 39 out.open(ifile + ".copy"); //调用open() 40 if(out); //如果流没错 41 out << ss; 42 out.close(); //没有close()下面error close关闭流,一个流不能重复打开多个文件 43 out.open("file"); 44 if(out) 45 std::cout << out.good() << endl; 46 else 47 std::cerr << "error" << std::endl; 48 }
课后题8.5
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 #include <vector> 5 6 using namespace std; 7 8 int main(int argc, char *argv[]) 9 { 10 string s; 11 fstream input(argv[1]); 12 vector<string>ivec; 13 14 while(!input.eof())//如果去掉if(input.fail())末尾会多输出一次 15 { 16 input >> s; 17 if(input.fail()) 18 { 19 cout << "fail " << endl; 20 break; 21 } 22 ivec.push_back(s); 23 } 24 25 for(string &s : ivec) 26 { 27 cout << s << endl; 28 } 29 }
没有if(input.fail( ))检查时,末尾总是会多输出一次文件末尾的内容。
看代码,文件每次读取到s中,然后push_back(s),当文件读取到最后一次时(它自己不知道读取到最后了),push_back(s)后再次读取,
发现到达末尾了,此时s中依然存着上一次的内容,push_back(s)后退出循环,所以最后一行s被push了两次,输出时当然输出两遍了。
7.文件模式
1 fstream fs("文件名", mode); //mode就是文件模式 2 3 in 以读的方式打开 4 out 以写的方式打开 5 app 每次写操作前均定位到末尾 6 ate 打开文件后立刻定位到文件末尾 7 trunc 截断文件 8 binary 以二进制方式IO
<1.只对ofstream或fstream设置out模式,out模式打开会丢弃已有数据
<2.只对ifstream或fstream设置in模式
<3.只有当out也被设定时才可以设定trunc模式
<4.只要trunc模式没被设定,就可以设定app模式,在app模式下即使没有规定out也默认为out模式
<5.没有指定trunc,以out打开也会截断文件,除非指定app,或同时打开in模式
<6.ate和binary可以用于任何类型的文件流对象,且可以和其他任何文件模式组合。
<7.每次调用open都可以指定文件模式。
8.sstream流
1 三个IO 2 istringstream 3 ostringstream 4 stringstream 5 特有操作 6 sstream strm; strm是一个未绑定的stringstream对象 7 sstream strm(s); strm是一个sstream对象,保存s的一个拷贝,此构造函数是explicit的 8 strm.str(); 返回strm所保存的string的拷贝 9 strm.str(s); 将string s拷贝到strm中,返回void
使用istringstream流
istringstream读字符串时遇见空格算一个字符串,就可以读一个句子的单词。
1 #include <iostream> 2 #include <vector> 3 #include <sstream> 4 #include <string> 5 #include <fstream> 6 7 using namespace std; 8 9 class record 10 { 11 public: 12 string name; 13 vector<string>phone; 14 }; 15 16 int main(int argc, char *argv[]) 17 { 18 ifstream input(argv[1]); 19 string s; 20 vector<record>Info; 21 while(getline(input, s)) 22 { 23 record re; 24 istringstream is(s); 25 is >> re.name; 26 cout << "eof" << is.eof() << endl; 27 while(!is.eof()) //while(is >> s)另外一种方法,不用测试eof,且注意eof成立为1 28 { 29 string s; 30 is >> s; 31 re.phone.push_back(s); 32 } 33 Info.push_back(re); 34 } 35 36 for(record &rec : Info) 37 { 38 cout << rec.name << " "; 39 for(string &ph : rec.phone) 40 { 41 cout << ph << " "; 42 } 43 cout << endl; 44 } 45 }
使用ostringstream流
ostringstream流里面可以保存字符串的拷贝。需要时输出。
比如检查一个字符串是否正确,正确的保存到流里,不正确的不保存,等到结束时输出全为正确的。
1 string st = "asdasdasdasd"; 2 string st1; 3 ostringstream os; 4 os << " " << st<< endl; //都写到了流里面 5 os << " " << st << endl; 6 cout << os.str() << endl; //输出字符串流里面保存的字符串,返回 字符串的拷贝 7 os.str(""); //使用前清空 8 os.str(st); 9 cout << os.str() << endl;
小结:
1.iostream处理控制台IO
2.fstream处理命名文件IO
3.stringstream完成内存string的IO
4.类fstream和sstream都继承自iostream,所以iostream的操作这些类也可以。
5.每个流都有一组条件状态,保证流正确无误的使用就要保证流的状态正确无误
6.为什么可以while(cin)来判断流状态,因为cin对象中重载了bool转换操作,如operator bool(),所以cin才能够参加布尔运算.