C++ primer 第八章
8.1 IO类
IO 库类型和头文件
头文件 | 类型 |
IOStream | IStream 从流中读取,OStream 向流中写入 |
Fstream | Ifstream 从文件中读取,Ofstream 向文件中写入 |
Sstream | Istringstream 从string对象读取,Ostringstream 向对象写入 |
IO 对象不可拷贝或赋值
std::ofstream out1, out2; out2 = out1;//错误,流对象不能赋值 std::ofstream print(out1);//错误,流对象不能拷贝,形参和返回值类型不能为流
读写一个IO对象会改变其状态,故传递和返回一个IO对象的引用不能为 const
IO 类所定义的函数和标志,可帮助控制流状态
便于理解,iostate 为一个对象,包含以下的位集合
goodbit = 0X0; eofbit = 0X1; failbit = 0X2; badbit = 0X4;
有时候我们需要知道流为什么失败
iostate 类型提供了表达流状态的完整功能,这个类型应作为一个位集合来使用
//用以说明,流的状态是可以像变量一样保存的 auto old_state = std::cin.rdstate(); //记录当前cin状态 std::cin.clear(cin.);;//将所有的条件状态复位,使流有效 process_input(cin);//do something 使用cin std::cin.setstate(old_state);//将cin置为原来状态 //复位failbit和badbit,其它标志位保持不变(没懂) cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);
每个输出流都管理一个缓冲区
操作系统会利用缓冲机制在需要时将程序的多个输出操作合并,提升性能
std::cout << std::cin.rdstate() << std::endl; std::cout << "HI" << std::endl; //输出HI和换行,然后刷新缓冲区 std::cout << "HI" << std::flush;//输出HI,然后刷新缓冲区 std::cout << "HI" << std::ends;//输出HI和一个空字符,然后刷新缓冲区 //通过设置unitBuf操作符来控制是否立即刷新 std::cout << std::unitbuf; //所有输出操作会立即刷新缓冲区 std::cout << std::nounitbuf;//回到正常的缓冲方式
如果程序崩溃,输出缓冲区不会被刷新
每个流同时最多关联到一个流,但多个流可以同时关联到一个 ostream
如下例,std::cin 与 std::cout 绑定(关联)
int ival; std::cout << "请输入一个整数 : "; std::cin >> ival;
std::cin.tie(&std::cout);//标准库内置 std::ostream* old_tie = std::cin.tie(nullptr);//cin不再与其它流关联 std::cin.tie(&std::cerr);//读取cin将会刷新cerr的缓冲区 std::cin.tie(old_tie);//重新将cin和cout关联
8.2 文件输入输出fstream
除继承 iostream 之外的操作
std::ifstream in("E:/2.txt");//构造一个ifstream并打开指定文件 std::ofstream out;//输出文件流,未关联到任何文件 out.open("E:/2.txt");//打开指定文件 if (out){ //检查是否open成功 std::cout << "HI" << std::endl; } in.close();//关闭文件
cin >> 内存 写入 ,内存 >> cout 读出
//写入(从内存读出) int ar[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, br[10]; ofstream ofile; ofile.open("E:/TEST.txt", ios::out); if (ofile) { for (int t =0; t<sizeof(ar) / sizeof(int); ++t) ofile << ar[t] << " ";//向文件内写入 } ofile.close(); //读取(向内存写入) ifstream ifile; ifile.open("E:/TEST.txt", ios::in); if (ifile) { for (int t =0; t<10; ++t) ifile >> br[t];//从文件内读出并写入br中 } ifile.close();
自动构造和析构
虽然 input 同名,但却不是同一个输出流,在离开作用域后,流将被自动析构
//argv main函数的参数 argc 参数个数 for( auto p = argv+1; p!= argv+argc; ++P){ ifstream input(*p);//创建输出流 if(input){ process(input); }else{ std::cerr << "can not open : "+ std::string(*p); } }
每个流都有一个关联的文件模式 (file mode)
以 out 模式打开的文件会丢弃已有数据,保留 ofstream 打开文件已有数据的方法是指定 app 或 in 模式
ofstream out1("E:/TEST1.txt"); //隐含以输出模式打开并截断文件 ofstream out2("E:/TEST2.txt", ofstream::out);//隐含截断文件 ofstream out2("E:/TEST3.txt", ofstream::out | ofstream::trunc);//按位或 //为了保留文件内容,必须显式指定app模式 ofstream app1("E:/TEST1.txt", ofstream::app);//隐含输出模式 ofstream app2("E:/TEST2.txt", ofstream::out | ofstream::app); ofstream out4;//未指定打开模式 out4.open("E:/TEST4.txt");//模式隐含为输出和截断 out4.close(); out4.open("E:/TEST4.txt", ofstream::app);//模式为输出和追加 out4.close();
8.3 string 流
String 流
从 string 读写数据,就像 string 是一个 IO 流一样
struct PersonInfo{ string name; vector<string> phones; } string line,word;//分别用来保存输入的一行和单词 vector<PersonInfo> people;//保存所有的输入记录 while(getline(cin,line)){ PersonInfo info; istringstream record(line); record >> info.name; while(record >> word){ info.phones.push_back(word); } people.push_back(info); } /* istringstream 向内存写入,ostringstream 从内存输出 */ for(const auto& entry : people){ ostringstream formatted,badNums; for(const auto& nums : entry.phones){ if(!valid(nums)){ badNums << " " << nums;//将数以字符串的形式存入 }else{ formatted << " " << format(nums);//将格式化的数字存入 } } if(badNums.str().empty()){ //没有错误的数 //打印名字和格式化的数字 cout << entry.name << " " << formatted.str() << endl; }else{ cerr << entry.name << " " << badNums.str() << endl; } } 将格式化的数字存入 } } if(badNums.str().empty()){ //没有错误的数 //打印名字和格式化的数字 cout << entry.name << " " << formatted.str() << endl; }else{ cerr << entry.name << " " << badNums.str() << endl; } }