C了个++:01 - C++的输入、输出和文件

来源:互联网 发布:淘宝店能赚钱吗 编辑:程序博客网 时间:2024/05/07 03:52

简单总结梳理了C++中有关文件输入输出的知识,并添加程序实例(实例见最后)

例程2:2016年华为软件编程大赛文件(topo.csv、demand.csv、result.csv)的输入输出(删减)

涉及知识:类、函数重载、模板、多重继承、流操作、缓冲区、控制符和格式化常量、类型转换

学习目的:文本文件输入输出、控制输出格式

使用的类:iostream(标准输入输出类)、fstream(文件输入输出类)、sstream(string字符串输入输出类)




流操作:(C++把输入输出看作字节流,通过对字节流对象进行操作来对数据做相应的输入输出处理)

  • 流是由字符字节(char)组成的数据,流操作(方法)的本质是类型转换操作
  • 将流与程序关联起来(声明输入流/输出流对象)
  • 将流与文件关联起来(将输入流/输出流对象与文件关联起来,调用成员函数open(char*)方法)
  • 以上两个关联可以通过声明初始化一步完成,流的使用方法同标准的流cin/cout


缓冲区:(内存块,设备与程序之间的临时存储工具,帮助匹配两者不同的传输速率)
  • 键盘输入时,通常是按下回车键时,刷新输入缓冲区
  • 屏幕输出时,通常是发送换行符时,刷新输出缓冲区;有输入到来时,也可能会刷新输出缓冲区
  • 使用控制符endl和flush


继承关系:作图
  • ios_base类(流的一般特征:格式化常量等)    --->    ios类(包括一个指向streambuf对象的指针)    --->    istream类/osteam类(方法)    --->    iostream类    --->    fstream类    --->    ifstream类/ofstream类
  • sstream类的继承关系流程同fstream类    --->    istringstream类/ostringstream类
  • streambuf类(管理输入/输出缓冲区的内存的类)
  • 创建类对象,会打开一个流,并自动创建一个属于各自对象的缓冲区;包含iostream头文件会自动创建8个流对象(cin/cout/cerr/clog)(4个用于窄字符流,4个用于宽字符流)和一些对象代表流(它是与流特性相关的数据成员)
  • 其它,将iostream类在std名称空间中,istream类和ostream类都是模板char具体化的typedef


输出格式化:

  • cout<<输出、ostream & put(char)、ostream & write(char*,int),类型转换为char以文本字符格式输出(参数为普通基本类型或const的char指针或void*),另外write()不会遇到空字符停止打印字符
  • ios_base类中的成员函数setf()方法(可调整单个格式标记信息)两个原型:fmtflags setf(fmtflags);  fmtflags setf(fmtflags,fmtflags);  (fmtflags是bitmask类型),该方法需结合使用格式化常量;fmtflags类型也是在ios_base类中定义的;另外,unsetf()方法原型:void unsetf(fmtflags);
  • 标准控制符:计数系统控制(dec/oct/hex:basefield)数字显示控制(fixed/scientific:floatfield)对齐控制(left/right/internal:adjustfield)等等
  • 3个最常用的控制符(头文件iomanip):setprecision(int)、setfill(char)、setw(int)分别设置精度、填充字符、字段宽度,相对于precision(int)、fill(char)、int width(int)使用更方便
  • 控制符(运算符函数重载)可用cout语句进行连接,控制符是函数,但不是成员函数,不能通过对象调用使用;控制符位于名称空间std中
  • 默认显示模式的精度是总位数,fixed/scientific显示模式的精度是小数位数
  • cout.setf(ios_base::showpoint);可显示末尾的0,显示的多少取决于显示模式和精度,显示的数字位数与数字被存储时的精度没有任何关系


输入:(给程序提供数据)
  • cin>>输入,格式化输入函数(参数为基本类型引用char指针),跳过空白(空格、换行符、制表符)
  • ios_base类中数据成员流状态(eofbit/badbit/failbit/goodbit)iostate类型(也是bitmask类型)是在ios_base类中定义的,这3个状态位都为0时,说明一切正常
  • 常用的设置流状态方法:good()、clear(iostate s=0)(无参可以用于重新打开输入)、setstate(iostate s);设置流状态位有一个非常重要的后果,流将对后面的输入或输出关闭,直到位被清除
  • 非格式化输入成员函数istream & get(char &)、int get(void)单字符输入,不跳过空白
  • 非格式化输入成员函数get(char*,int+1,char)、getline(char*,int+1,char)读取字符串进行输入,不跳过空白;第三个参数省略的话,默认将换行符用作分界字符;读取最大数目的字符或分界字符后为止;两者的主要区别是是否保留分界符,getline(char*,int+1,char)不保留分界符
  • 非预期设置failbit,流被破坏设置badbit,文件尾设置eofbit;文件尾符号常量EOF(头文件iostream中定义),cin.get(ch)遇文件尾转换为false  并设置failbit,ch=cin.get()遇到文件尾输出EOF的值;文件尾或空行(即没有读取任何字符),get(char*,int+1,char)都会设置failbit,文件尾(或特殊空行)或读取最大数目的字符(且行中还有字符)时,会让getline(char*,int+1,char)设置failbit;while(getline(cin,str)&&str.size()>0){}空行处理方法
  • cin.ignore(int,'\n');可用于删除行中剩余的字符;char cin.peek();(==get(char)+putback(char))可用于查看下一个字符,但不抽取,可以用于多种条件(多个分界符)同时分割字符串;read()不会添加空字符,对应于write()
  • 其它,getline(cin/fin/instr,string str,char ch)很方便


文件关联:
  • 包含头文件fstream就可以,不必包含iostream,因为继承关系,但cin/cout/cerr对象的使用需要iostream头文件
  • 默认模式打开文件进行输出将自动把文件的长度截短为零,即删除已有内容
  • 输入输出流对象过期时(即程序终止时),文件关联自动关闭,或用close()方法类似显式关闭;换其它文件进行关联需要显式使用close()方法;没有打开文件的话,不需要进行关闭关联,因为没关联上
  • 关闭关联并不会删除流对象,则可以将流重新关联同一文件或另一文件,可能会需要cin.clear()方法重新打开输入,这取决于将文件与ifstream对象关联起来时,是否自动重置流状态,使用cin.clear()方法是无害的;关闭文件关联会刷新缓冲区
  • 成员函数open(char*)方法以及ofstream的构造函数和ifstream的构造函数,参数指向char的指针(地址)是C-风格字符串,有必要将以string对象为文件名的字符串使用c_str()方法来转换为C-风格的字符串
  • 打开一个不存在的文件进行输入时,将设置failbit,一种更好的检查文件是否被打开的方法is_open()
  • 多文件处理策略取决于是同时处理使用还是依次处理使用,少开一些流会节省计算机资源
  • 命令行参数(指定文件)int main(int argc,char* argv[])注意[]在argv后面,即第二个参数为char(* argv)[],argc是命令行参数个数,char(* argv)[]是一个指向char的指针数组(地址数组)


文件模式:(描述文件如何被使用:读、写、追加)
  • 文件流的构造函数和i/ofstream open(char*,openmode mode=ios_base::in/out|trunc)方法,第二参数指定文件模式;注意,fstream类不提供默认的文件模式值,创建该对象必须显式提供第二参数
  • openmode类型与fmtflags类型和iostate类型一样,都是一种bitmask类型,在ios_base中定义;文件模式常量(in/out/ate/app/trunc/binary);文件模式错误能够被is_open()方法检测出来


内核格式化:(负责程序和string对象的I/O)
  • iostream族负责程序与终端之间的I/O;fstream族负责程序与文件之间的I/O;sstream族负责程序与string对象之间的I/O;接口使用方法相同
  • string对象本质是文本文件,所以所对应的流操作依然是类型转换,也叫格式化信息操作(内核格式化)
  • ostringstream中成员函数string str();返回一个被初始化为缓冲区内容的字符串对象,该方法可以“冻结”该对象
  • getline(cin/fin/instr,string,char)很方便,因为string类很好用
  • string字符串关联,istringstream流对象可以使用string对象进行初始化,如istringstream instr(str);




例程1:实现命令行参数读取文件,并对文件中的字符数进行统计求和
#include<fstream>#include<iostream>// for cerr/cout//#include<cstdlib>// for exit()int main(int argc,char* argv[])// 命令行参数,argv[i]是第 i 个指向 char 的指针(地址){using namespace std;if (argc == 1)// quit if no arguments{cerr << "Usage: " << argv[0] << " filename[s]\n";exit(EXIT_FAILURE);}ifstream fin;// 开一个文件输入流long count;long total = 0;char ch;for (int i = 1;i < argc;i++){fin.open(argv[i]);// 关联第 i 文件cout << i << " | " << argc << "   ";if (!fin.is_open())// 没有打开就不用使用close(){cerr << "Can't open " << argv[i] << endl;fin.clear();// 重新打开输入continue;}count = 0;while (fin.get(ch))count++;// 统计文件中的字符数cout << count << " character in " << argv[i] << endl;total += count;fin.clear();// 重新打开输入fin.close();// 换文件需要先断开之前的文件  disconect file}cout << total << " charaters in all files.";return 0;}



例程2:2016年华为软件编程大赛文件(topo.csv、demand.csv、result.csv)的输入输出(删减)
#include<iostream>#include<fstream>#include<sstream>#include<string>#include<iomanip>// for setw() setfill()int main()//int argc,char* argv[])// 命令行参数{using namespace std;ifstream file_topo("F:\\topo.csv");//argv[1]);// 关联 topo.csv 文件string topo_line;string topo_num[4];int top_num[4];if (file_topo.good())// check{while (getline(file_topo,topo_line))// read one line{istringstream strl_topo(topo_line);// 将每一行初始化一个字符串输入流对象for (int i = 0;i < 4;i++){getline(strl_topo,topo_num[i],',');// 按“,”进行分割top_num[i] = atoi(topo_num[i].c_str());// 转换为 int 类型,为之后比较大小cout << setw(2) << setfill('0') << top_num[i] << " ";// 格式化输出}cout << endl;}}cout << "*********************************" << endl;file_topo.close();//file_topo.clear();ifstream file_demand("F:\\demand.csv");//argv[2]);// 关联 demand.csv 文件string demand_line;string demand_sec[3];string demand_num[52];int num_len = 0;if (file_demand.good()){if (getline(file_demand,demand_line)){istringstream strl_demand(demand_line);for (int i = 0;i < 3;i++){getline(strl_demand,demand_sec[i],',');}istringstream strsec_demand(demand_sec[2]);int i = 2;while (getline(strsec_demand,demand_num[i],'|')){i++;}num_len = i;}}int* dem_num = new int[num_len];// 动态数组dem_num[0] = atoi(demand_sec[0].c_str());cout << dem_num[0] << " ";dem_num[1] = atoi(demand_sec[1].c_str());cout << dem_num[1] << " ";for(int i = 2;i < num_len;i++){dem_num[i] = atoi(demand_num[i].c_str());cout << dem_num[i] << " ";}cout << endl << "*********************************" << endl;file_demand.close();//file_demand.clear();    ofstream file_result;// 关联 result.csv 文件    file_result.open("F:\\result.csv",ios::out|ios::trunc);//argv[3],ios::out|ios::trunc);file_result << setw(2) << setfill('0') << dem_num[0];for(int i = 1;i < num_len;i++){file_result << "|" << setw(2) << setfill('0') << dem_num[i];}file_result.close();//file_result.clear();return 0;}




本文总结自《C++ primer plus》(第六版中文版)第十七章:输入、输出和文件
2 0