CH8 IO库
8.1
1 #include <iostream> 2 #include <string> 3 #include <sstream> 4 #include <fstream> 5 6 int main() 7 { 8 //cout是一个输出流对象,位于std空间,cin是一个输入流对象 9 std::cout << "Hello C++ Primer" << std::endl; 10 std::string word; 11 std::cin >> word; 12 std::fstream fs;//fs是一个fstream对象 13 std::stringstream ss;//ss是一个stringstream对象 14 return 0; 15 }
IO类型间的关系
C++语言不直接处理输入输出,而是通过一族定义在标准库中的类型来处理IO,这些类型支持从设备读取、向设备写入数据的IO操作,设备可以是文件,控制台窗口等。这些类型定义在三个独立的头文件中:iostream,fstream,sstream.9个标准库类型(宽字符版本算在统一的普通版本中),继承关系如下
IO对象不能拷贝或赋值
1 void print(std::ofstream of) 2 { 3 std::cout << "IO test " << std::endl; 4 } 5 void print_re(std::ofstream& of) 6 { 7 std::cout << "IO referience or pointer" << std::endl; 8 }
1 std::ofstream print(std::ofstream& of) 2 { 3 std::cout << "IO referience or pointer" << std::endl; 4 std::ofstream of2; 5 return of2; 6 }
9 int main() 10 { 11 //cout是一个输出流对象,位于std空间,cin是一个输入流对象 12 std::cout << "Hello C++ Primer" << std::endl; 13 std::string word; 14 std::cin >> word; 15 std::fstream fs;//fs是一个fstream对象 16 std::stringstream ss;//ss是一个stringstream对象 17 //IO对象不能赋值,下面的赋值操作是错误的 18 std::ofstream out1; 19 std::ofstream out2; 20 out1 = out2; 21 //IO对象也不能拷贝 22 print(cout);//由于传值是拷贝操作,而IO对象不能拷贝,所以这样写是错误的 23 //但IO操作可以通过引用或指针传递和返回流 24 print_re(cout); 25 return 0; 26 }
由于IO对象不能拷贝,所以不能放到对象中,因为保存在容器中的对象都必须可以复制
8.1.2 条件状态
IO操作一个与生俱来的问题就是可能发生错误,IO类定义了一些函数和标志,可以帮助我们访问和操纵流的条件状态。badbit:系统级错误,通常情况下,一旦badbit被置位,流就无法使用了。failbit,eofbit,被置位,还可以修正,流还可以继续使用。
1 int ival; 2 std::cin >> ival;
如上述要求输入一个int型变量,若输入一个char型,则读操作就会失败,cin就会进入错误状态
1 void check_cin_state(std::istream& is) 2 { 3 if (is.bad()) 4 std::cout << "cin is bad" << std::endl; 5 else 6 std::cout << "cin is not bad" << std::endl; 7 if (is.fail()) 8 std::cout << "cin is fail" << std::endl; 9 else 10 std::cout<<"cin is not fail" << std::endl; 11 if (is.eof()) 12 std::cout << "cin is eof" << std::endl; 13 else 14 std::cout << "cin is not eof" << std::endl; 15 if (is.good()) 16 std::cout << "cin is good" << std::endl; 17 else 18 std::cout << "cin is not good" << std::endl; 19 } 20 int main() 21 { 22 std::cout << "check the state of cin" << std::endl; 23 check_cin_state(std::cin); 24 std::cout << "please input an integer" << std::endl; 25 int a; 26 std::cin >> a; 27 std::cout << "check the state of cin again" << std::endl; 28 check_cin_state(std::cin); 29 system("pause"); 30 return 0; 31 }
要求输入的是int型,输入char型,流状态是fail。
管理流状态
上述一旦流状态is notgood就结束了,可以通过恢复流状态继续使用流
计算输入数字的和的程序为例、
1 int sum = 0; 2 int ival; 3 /*while (std::cin >> ival) 4 { 5 sum += ival; 6 std::cout << "sum is " << sum << std::endl; 7 }*/ 8 //上述写法一旦输入类型不是int型就结束了,所以可以通过管理流使得流可以继续使用 9 while (std::cin >> ival, !std::cin.eof()) 10 { 11 if (std::cin.bad()) 12 throw std::runtime_error("IO stream is not successful"); 13 if (std::cin.fail()) 14 { 15 std::cout << "IO stream is fail,try again" << std::endl; 16 std::cin.clear();//恢复流状态 17 std::cin.ignore(100, '\n'); 18 continue; 19 } 20 sum += ival; 21 std::cout << "sum is " << sum << std::endl; 22 }
8.2 文件输入输出
头文件fstream定义了三个类型来支持文件IO:ifstream 从 一个给定文件读取数据ofstream向一个给定文件写入数据,发stream可以读写给定文件。
8.2.1文件流的使用
创建文件流对象时,如果提供了文件名,会自动调用open函数打开文件
1 ofstream outfile("test.txt");//创建一个输出流对象outfile,并使用该对象创建一个文件 2 outfile << "Hello C++" << endl;//使用流向文件写入内容 3 outfile.close();//关闭流 4 string txt("t.txt");//文件夹中需要有此文件 5 ifstream infile(txt);//C++11新标准,旧版本需使用C风格字符串infile(txt.c_str())
如果定义了一个了一个空文件(文件流对象没有绑定文件),可以调用open将它与文件关联起来如果调用失败,failbit会被置位,所以进行是否成功检查是一个好习惯
1 ifstream infile; 2 infile.open("t.txt"); 3 if (infile) 4 { 5 string str; 6 while(infile >> str) 7 cout << str << endl; 8 infile.close(); 9 }
8.3 string流
sstream 头文件定义的三个类型是支持内存IO,即字符串流是内存中的输入输出。
istringstream从string读取数据,ostringstream向string读数据stringstream既可以从string读数据也可以向string写数据。
使用istringstream
当某些工作需要处理某行文本中的单个单词时通常可以使用istringstream
教材中有这样一个例子,家丁一个通讯录文件,列出了一些人和他们的电话,但是有些人会有不止一个电话号码,通常文件的每条记录(每一行)都是以人名开头,后面跟随他们的电话
eg: 小王 130xxxxxxxx 6283xxxx
小李 158xxxxxxxx
那么首先需要定义一个类PersonInfo来描述输入数据,PersonInfo有一个对象name来表示人名,一个vector<string>来保存电话号码。该程序需要读取兵处理多条PersonInfo记录,所以需要一个vector<PersonInfo>来保存多条记录,使用getline函数读取每一行,使用istringstream读取行中的单个单词。
1 struct PersonInfo { 2 std::string name; 3 std::vector<std::string> phone; 4 void save_contacts(); 5 }; 6 7 8 void PersonInfo::save_contacts() 9 { 10 string line;//保存来自输入的一行记录 11 string word;//保存记录的单个单词 12 vector<PersonInfo> person;//保存输入的所有记录 13 14 while (getline(std::cin, line))// 15 { 16 PersonInfo info;//创建一个PersonInfo的对象,保存记录 17 istringstream record(line);//创建一个isrting流对象record,并绑定到输入的记录的行 18 record >> info.name;//读取一条记录中的名字给PersonInfo对象的name成员 19 while (record >> word)//读到的记录中的电话号码给PersonInfo对象的phone成员 20 info.phone.push_back(word); 21 person.push_back(info);//将读到的所有记录保存到vector<PersonInfo>中 22 } 23 }
使用ostringstream
1 //使用ostringstream对构造好的输入的记录进行输出 2 for (const auto entry : person) 3 { 4 //创建ostring对象用于输出无效的电话号码和规范为特定格式的后的电话号码 5 ostringstream badNums; 6 ostringstream formatted; 7 // 8 for (const auto& nums : entry.phone) 9 { 10 if (!valid(nums)) 11 badNums << " " << nums; 12 else 13 formatted << " " << format(nums); 14 } 15 if (badNums.str.empty())//如果电话号码有效,输出格式化后的电话号码 16 cout << entry.name << " " << formatted.str(); 17 else 18 cerr << "input error :" << endl; 19 }
上面的是处理的从cin读取的数据,可以综合文件流和string流实现从文件读取数据
小结
C++使用标准库类来处理面向流的输入输出
iostream 处理控制台IO
fstream 处理命令文件IO
stringstream完成内存string的IO
继承关系如上面的图
每个IO对象都维护一组条件状态,用来指出此对象上是否可进行IO操作,如果遇到了错误,则对象状态变为失效,除非纠正错误才能执行后续操作