• C++面向对象程序设计(第2版)第七章(输入输出流)知识点总结


    C++面向对象程序设计

    参考书目:《C++面向对象程序设计》—— 谭浩强 《C++程序设计:思想与方法》—— 翁惠玉



    一、标准输入流

           在C++中,输入输出是通过流完成的。C++的输出操作将一个对象的状态转换成一个字符序列,输出到某个地方。输入操作则是从某个地方接收一个字符序列,然后将其转换成一个对象的状态所要求的格式。
           把接收存放输出数据的地方叫做目标,把输入数据来自的地方叫做源,输入和输出操作可以看成字符序列在源、目标和对象之间的流动。执行输入和输出操作的类体系叫做流类,提供流类实现的系统叫做流类库。

    在这里插入图片描述

    注:此图是简化的流类库的基本类等级图,而不是直接的继承关系图。其实它们都是模板类,箭头表示的是类等级关系。
    这个等级关系在头文件iostream.h中说明。在图中,ios类中一个指针成员指向streambuf类的对象。Streambuf类管理流的缓冲区。由于数据隐蔽和封装的需要,普通用户只使用ios、istream和ostream类提供的公有接口,完成流的提取和插入操作。
    ios类是istream类和ostream类的虚基类,提供对流的格式化I/O操作和错误处理的成员处理的成员函数。从ios类公有派生istream类和ostream类分别提供对流进行提取和插入操作的成员函数,而iostream类通过组合istream类和ostream类支持对一个流进行双向操作,它并没有提供新成员函数。
    iostream中预定义四个流对象,它们分别是cin、cout、cerr、clog。事实上可以将cin视为istream的一个对象,而cout视为ostream的一个对象。流是一个抽象的概念,当实际进行I/O操作时,必须将流和一个具体的物理设备连接起来。
    

           C++的流类库预定义的四个流所连接的设备如表所示:

    设备
    cin标准输入设备
    cout标准输出设备
    cerr标准错误输出设备(非缓冲方式)
    clog标准错误输出设备(缓冲方式)

           与iostream类库有关的头文件:iostream类库中不同的类的声明被放在不同的头文件中,用户在程序中用#include命令包含所需的头文件就相当于在程序中声明了所需的类。

    • iostream       输入输出流操作

    • fstream       管理文件的I/O操作

    • strstream       字符流的I/O操作

    • stdiostream       混合使用C和C++

    • iomanip       使用格式化I/O操作

      注:已在iostream头文件中重载了>> 和<<运算符。用于标准类型数据的输入和输出。
      

    二、标准输出流

    1.用控制符设置输出流中的数据基数

           ostream类定义了cout、cerr和clog三个流对象。

    1. cout流对象
      cout流在内存开辟一个缓冲区存放流中的数据,当遇到endl时,立即输出流中的所有数据,然后插入一个换行符并清空缓冲区。
    2. cerr流对象
      cerr流对象是标准出错流。它向标准出错设备输出有关出错的信息。

    例:编写程序,从键盘输入a,b,c的值求解一元二次方程 ax2+bx+c=0。如果a=0或判别式的值b2-4ac<0,输出出错信息。

    int main()
    {
    	float  a, b, c, disc;
    	cout << "请输入 a,b,c:";
    	cin >> a >> b >> c;
    	if (a == 0)
    		cerr << "a 等于0,错误!" << endl;
    	else if ((disc = b * b - 4 * a*c) < 0)
    		cerr << "判别式b*b-4*a*c<0"<<endl;
    	else
    		{
    			cout << "x1=" << (-b + sqrt(disc)) / (2 * a) << endl;
    			cout << "x2=" << (-b - sqrt(disc)) / (2 * a) << endl;
    		}
    	return 0;
    }
    

    在这里插入图片描述

    1. clog流对象
      clog流对象也是标准出错流,它与cerr的区别是: cerr不经过缓冲区直接向显示器输出出错信息,clog把出错信息存放在缓冲区,当缓冲区满或遇到endl时向显示器输出出错信息。
      在这里插入图片描述

    例:用控制符控制输出格式。

    #include 
    #include 
    #include
    using namespace std;
    int main()
    {
    	int a;
    	cout << "input a:";
    	cin >> a;
    	cout << "dec:" << dec << a << endl;
    	cout << "hex:" << hex << a << endl;
    	cout << "oct:" << setbase(8) << a << endl;
    	char *pt = (char *)"China";
    	cout << setw(10) << pt << endl;
    	cout << setfill('*') << setw(10) << pt << endl;
    	double pi = 22.0 / 7.0; cout << setiosflags(ios::scientific) << setprecision(8);
    	cout << "pi=" << pi << endl;
    
    	cout << "pi=" << setprecision(4) << pi << endl;
    	cout << "pi=" << setiosflags(ios::fixed) << pi << endl;
    	return 0;
    }
    

    在这里插入图片描述

    2.用流成员函数设置输出流中的数据基数

           用于控制输出格式的常用成员函数见表7.4:
    在这里插入图片描述
           流成员函数setf和控制符setiosflags括号中参数是格式标志,在类ios中定义它是枚举值。所以在引用这些格式标志时要以ios::开始,格式标志列于表7.5:
    在这里插入图片描述

    例:用流控制成员函数输出数据。

    int main()
    {
    	int a = 21;
    	cout.setf(ios::showbase);
    	cout << "dec:" << a << endl;
    	cout.unsetf(ios::dec);
    	cout.setf(ios::hex);
    	cout << "hex:" << a << endl;
    	cout.unsetf(ios::hex);
    	cout.setf(ios::oct);
    	cout << "oct:" << a << endl;
    	char *pt =(char *) "China";
    	cout.width(10);
    	cout << pt << endl;
    	cout.width(10);
    	cout.fill('*');
    	cout << pt << endl;
    	double pi = 22.0 / 7.0;
    	cout.setf(ios::scientific);
    	cout << "pi=";
    	cout.width(14);
    	cout << pi << endl;
    	cout.unsetf(ios::scientific);
    	cout.setf(ios::fixed);
    	cout.width(12);
    	cout.setf(ios::showpos);
    	cout.setf(ios::internal);
    	cout.precision(6);
    	cout << pi << endl;
    	return 0;
    }
    

    在这里插入图片描述

    三、流对象

    1.cin 流

           cin是istream类的对象,从标准输入设备读取数据。流提取运算符>>在流中提取数据时通常跳过流中的空格、tab键、换行符等字符。只有输入回车键时输入的数据才进入键盘缓冲区,形成输入流,提取运算符才能从其中提取数据。
           当遇到无效字符(与变量数据类型不一致)或文件结束符时,输入流cin就处于出错状态,此时对cin流的所有操作都被终止。当输入流出错时,cin的值是false,所以可以根据cin的值判断流对象是否处于正常状态。

    例:测试cin的值,判断流对象的状态。

    int main()
    {
    	float grade;
    	cout << "enter grade:";
    	while (cin >> grade)
    	{
    		if (grade >= 85)
    			cout << grade << " GOOD!" << endl;
    		if (grade < 85 && grade >= 60)
    			cout << grade << " Just so so!" << endl;
    		if (grade < 60)
    			cout << grade << " fail!" << endl;
    		    cout << "enter grade:";
    	}
    	cout << "The end." << endl;
    	return 0;
    }
    

    在这里插入图片描述

    2.输入字符的流成员函数

    1. 用get函数读入一个字符
      格式:cin.get()
      函数的类型是字符,函数的功能是从输入流中提取一个字符作为函数值返回。如在流中遇到文件结束符EOF时,返回-1。
    int main()
    {
    	char c;
    	cout << "enter a sentence:" << endl;
    	while ((c = cin.get()) != EOF)
    		cout.put(c);
    	return 0;
    }
    
    1. 带一个参数的get函数
      格式:cin.get(字符变量)
      从输入流中提取一个字符赋予字符变量。如遇到文件结束符就结束提取。输入回车后再输入文件结束符。
    int main()
    {
    	char c;
    	cout << "enter a sentence:" << endl;
    	while (cin.get(c)) { cout.put(c); }
    	cout << "end" << endl;
    	return 0;
    }
    
    1. 带三个参数的get函数
      格式: cin.get(字符指针,n,终止字符)
      n 与提取的字符个数相关。函数从键盘缓冲区最多顺序提取n-1个字符,顺序放入字符指针所指的字符数组。如果在提取过程中遇到终止字符,无论是否满足指定的字符个数都要终止提取。
    void main()
    {
    	char ch[20];
    	cout << "enter a sentence:" << endl;
    	cin.get(ch, 10, '\n');
    	cout << ch << endl;
    }
    
    1. 用成员函数getline读入一行字符
      格式:cin.getline(字符指针,n,终止字符)
      函数功能与带三个参数的get函数类似。
      带三个参数的cin.get和cin.getline的异同:
      相同的是它们都不忽略提取过程中遇到的空白字符,当遇到终止字符时就停止提取。
      不同的是停止提取时,cin.getline会把指针移到终止字符后相邻的字节,而带三个参数的cin.get函数不会。
    int main()
    {
    	char ch[20];
    	char c1;
    	cout << "输入一句话:" << endl;
    	cin >> ch;
    	cout << "第一次 cin 提取的字符串是:" << ch << endl;
    	c1 = cin.get();
    	cout << "第二次 cin.get() 提取的字符串是:" << c1 << endl;
    	cin.get(c1);
    	cout << "第三次 cin.get(c1) 提取的字符串是:" << c1 << endl;
    	cin.get(ch, 20, '/');
    	cout << "第四次 cin.get( ch, 20, '/') 提取的字符串是:" << ch << endl;
    	cin.getline(ch, 20, '/');
    	cout << "第五次 cin.getline (ch,20,'/')提取的字符串是:" << ch << endl;
    	cin.getline(ch, 20, '/');
    	cout << "第六次 cin.getline (ch,20,'/')提取的字符串是:" << ch << endl;
    	return 0;
    }
    

    在这里插入图片描述

    注:cin和cin.getline都具有从键盘缓冲区按指针所指提取字符串的功能。它们有以下区别:
    1.cin忽略起始的空白字符;而cin.getline不忽略起始的空白字符。
    2.cin当提取到非空白字符后,遇到空白字符时就终止提取,指针就停留在空白字符处;而cin.getline是提取到规定的终止字符或规定的字符个数后终止提取,指针停留在提取的最后一个字符后面相邻的字节。
    3.通过对>> 的重载,cin可以提取其他类型的数据;而cin.getline只能输入字符串。
    

    3.istream类其他函数

           eof()函数
           当输入缓冲区的指针遇到文件结束符时函数值为真,否则显假。从键盘用ctrl+z输入文件结束符。

    例:从键盘输入字符串,以文件结束符作为结束标志,逐个输出非空格字符。

    void main()
    {
    	char c;
    	while (!cin.eof())
    		if ((c = cin.get()) != '  ')
    			//cout.put(c);
    			cout << c;
    }
    

    四、文件操作和文件流

    1.文件的概念

           文件是指存储在存储介质上的数据集合。操作系统把存储介质上的相关数据抽象为文件,用标识符为其取名并由文件系统管理文件。只要用户指出文件名,操作系统就可以按名存取文件信息。根据文件中数据的表示形式,文件分为ASCII文件和二进制文件。ASCII文件就是文本文件,每个字节表示一个字符。二进制文件是把内存中的数据、指令按其在内存的格式存放在磁盘上。
           字符信息在内存也是以ASCII码形式存放,所以字符在ASCII码文件和在二进制文件中形式是一样的。对于数值数据,两者是不一样的。如,一个十进制整数100000,用二进制表示时用四个字节;而用ASCII码表示时用六个字节。
    在这里插入图片描述

    2.文件流类与文件流对象

           文件流是以外存文件为输入输出对象的数据流。输出文件流是从内存流向外存文件的数据流,输入文件流是从外存文件流向内存的数据流。为了弥补访问内存和访问外存的速度差,每个文件流都有一个内存缓冲区。
           在C++的I/O类库里定义了几种文件类,专门用于文件的输入和输出操作。
    在这里插入图片描述
           从图中看到C++从标准输入、输出流类派生出三个文件流类。

    • ifstream类       支持从磁盘文件输入。
    • ofstream类       支持向磁盘文件输出。
    • fstream类       支持对磁盘文件输出和输出。

           要对文件进行输入输出,必须定义一个文件流类对象,用对象调用类的成员函数对文件操作。

    3.文件的打开与关闭

    1. 打开磁盘文件
      打开文件有两种方法:
      (1)建立文件流对象,用对象调用类成员函数open。
      调用成员函数的一般形式:文件流对象.open(文件名,输入输出方式);
      例:ofstream outfile;
      outfile.open( “f1.txt”, ios::out);
      在这里插入图片描述
      (2)在定义文件流对象时指定参数
      调用成员函数的一般形式:文件流类 对象(文件名,输入输出方式);
      例:ofstream outfile ( “f1.txt”, ios::out);
    2. 关闭磁盘文件
      文件使用结束,必须关闭文件,用文件流对象调用关闭文件成员函数实现。
      格式: 文件流对象.close();
      功能:解除文件流对象与磁盘文件的关联。

    4.对ASCII码文件操作

           ASCII码文件也是文本文件,文件中一个字节存放一个字符。对ASCII码文件操作包括向文件写入字符和从文件读取字符。
           读写ASCII码文件有用文件流对象与提取、插入运算符和用文件流对象调用类的成员函数put, get, getline 两种方法。

    例:定义一个有十个元素的整型数组,从键盘输入十个整数,将它们放入数组,同时用插入运算符将它们写入当前目录下的f1.txt文件。

    #include 
    #include 
    #include
    using namespace std;
    int main()
    {
    	int a[10];
    	ofstream outfile("f1.txt");
    	if (!outfile)
    	{
    		cerr << "open error!" << endl;
    		exit(1);
    	}
    	cout << "enter 10 integer numbers:" << endl;
    	for (int i = 0; i < 10; i++)
    	{
    		cin >> a[i];
    		outfile << a[i] << " ";
    	}
    	outfile.close();
    	return 0;
    }
    

    在这里插入图片描述

    注:程序中用文件流类ofstream,它是在头文件fstream中定义的,用vc6.0时要把这个头文件包含进来。
    

    5.对二进制文件操作

           二进制文件是按内存中的数据存储形式写入磁盘文件,因此又称为内存数据的映象文件。
           对二进制文件操作与对文本文件操作相似的是先定义文件流对象,然后打开文件,使用完要关闭文件。在打开时必须指定文件的存储形式是二进制形式,二进制文件即可以作为输入文件也可以作为输出文件,还可以作为既能输入又能输出的文件。这是与ASCII文件不同的地方。

    1. 分别用成员函数read和write读写文件
      读二进制文件用istream类read成员函数;写二进制文件用ostream类write成员函数。
      它们的函数原型分别是:
      istream& read(char * bu, int len);
      ostream& write( const char * bu, int len);
      字符指针bu指向内存要读或写的数据起始位置。len是一次要读写的数据字节个数(数据长度)。
      调用格式:
      输入文件流对象.read( 内存指针, 长度);
      输出文件流对象.write( 内存指针, 长度);

    例:把一批数据以二进制形式写入磁盘文件,并从文件中读数据并显示到屏幕上。

    struct student
    {	char name[20];
     	int num;
     	int age;
     	char sex;
    };
    int main()
    { 
        student stud[3]={ "Li",   1001,18,'f',    "Fun",1002,19,'m', "Wang",1004,17,'f'};
        ofstream outfile("stud.dat",ios::binary);
        if(!outfile)  {  cerr<<"open error!"<<endl;      abort();  }
        outfile.write( (char *)&stud,sizeof(stud));
        outfile.close();
        return 0;
    }
    
    int main()
    {
    	student stud[3]; 	int i;
    	ifstream infile("stud.dat", ios::binary);
    	if (!infile) { cerr << "open error!" << endl;	abort(); }
    	infile.read((char*)stud, sizeof(stud));
    	infile.close();
    	for (i = 0; i < 3; i++)
    	{
    		cout << "NO." << i + 1 << endl;
    		cout << "姓名:" << stud[i].name << endl;
    		cout << "学号:" << stud[i].num << endl;
    		cout << "年龄:" << stud[i].age << endl;
    		cout << "性别:" << stud[i].sex << endl << endl;
    	}
    	return 0;
    }
    

    在这里插入图片描述

    1. 与文件指针有关的流类成员函数
      为了随机读取二进制文件中数据,磁盘文件用一个指针表示当前要访问的位置。每次读或写文件后会自动修改指针。使指针总是指向当前要访问的位置。对于二进制文件,允许程序控制指针移动,实现随机访问文件。文件流类提供了有关文件指针的成员函数。
      在这里插入图片描述
      函数参数文件中的位置和位移量以字节为单位,是长整型。参照位置表示以什么作为移动起点。
      ios类定义为:
      ios::beg       以文件开始为起点,这是默认值。
      ios::cur       以指针当前位置为起点。
      ios::end       以文件结尾为起点。
    2. 随机访问二进制数据文件
      利用流类的成员函数移动文件指针,实现随机访问文件中任何一个字节里的数据。

    例:有五个学生的数据,要求:
    1)把它们写入磁盘文件
    2)从磁盘文件读第1,3,5学生数据并显示
    3)修改第3 个学生的数据并保存到原来位置
    4)从磁盘文件读入修改过的5个学生数据并显示

    struct student
    {
    	int num;   char name[20];   float score;
    };
    int main()
    {
    	int i;
    	student stud[5] = { 1001,"Li",85,1002,"Fun",97.5,1004,"Wang",54,1006,"Tan",76.5,1010,"ling",96 };
    	fstream iofile("stud.dat", ios::in | ios::out | ios::binary);
    	if (!iofile)
    	{
    		cerr << "open error!" << endl;   abort();
    	}
    	for (i = 0; i < 5; i++)
    		iofile.write((char *)&stud[i], sizeof(stud[i]));
    	student stud1[5];
    	for (i = 0; i < 5; i = i + 2)
    	{
    		iofile.seekg(i * sizeof(stud[i]), ios::beg);
    		iofile.read((char *)&stud1[i / 2], sizeof(stud1[i]));
    		cout << stud1[i / 2].num << " " << stud1[i / 2].name << " "<< stud1[i / 2].score << endl;
    	}
    	cout << endl;
    	stud[2].num = 1012;
    	strcpy(stud[2].name, "Wu");
    	stud[2].score = 60;
    	iofile.seekp(2 * sizeof(stud[0]), ios::beg);
    	iofile.write((char *)&stud[2], sizeof(stud[2]));
    	iofile.seekg(0, ios::beg);
    	for (i = 0; i < 5; i++)
    	{
    		iofile.read((char *)&stud[i], sizeof(stud[i]));
    		cout << stud[i].num << " " << stud[i].name << " "<< stud[i].score << endl;
    	}
    	iofile.close();
    	return 0;
    }
    

    在这里插入图片描述

    五、字符串流

           字符串流以内存中用户定义的字符数组(字符串)为输入输出对象,即将数据写入内存数组,或从内存字符数组读取数据。
           字符串流也需要缓冲区,读取或写入时,流缓冲区中的数据不断增加,待缓冲区满或遇到换行符时,缓冲区中数据一起写入字符数组或赋予指定变量。

    1. 建立写字符串流对象
      ostrstream类的构造函数原型是:
      ostrstream::ostrstream( char *bu, int n, int mode =ios::out);
    2. 建立读取字符串流对象
      istrstream类的构造函数原型是:
      istrstream::istrstream( char *bu, int n);
      istrstream::istrstream( char *bu);
    3. 建立读、写字符串流对象
      strstream类的构造函数原型是:
      strstream::strstream( char *bu, int n,int mode);

    例:在一个字符数组c中存放10个整数,以空格为分隔符,要求将它们放到整型数组中排升序,然后再写入原来的字符数组中。

    #include 
    #include 
    #include
    #include
    using namespace std;
    int main()
    {
    	char c[50] = "12 34 65 -23 -32 33 61 99 321 32";
    	int a[10], i, j, t;
    	cout << "array c:" << c << endl;
    	istrstream strin(c, sizeof(c));
    	for (i = 0; i < 10; i++)
    		strin >> a[i];
    	cout << "array a:";
    	for (i = 0; i < 10; i++)
    		cout << a[i] << " ";
    	cout << endl;
    	for (i = 0; i < 9; i++)
    		for (j = 0; j < 9 - i; j++)
    			if (a[j] > a[j + 1])
    			{
    				t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
    			}
    	ostrstream strout(c, sizeof(c));
    	for (i = 0; i < 10; i++)
    		strout << a[i] << " ";
    	strout << ends;
    	cout << "array c:" << c << endl;
    	return 0;
    }
    

    在这里插入图片描述

  • 相关阅读:
    目录优先的图片库网站PiGallery2
    这么多年, Android 虚拟机到底做了些什么?
    时间空间复杂度分析--插入排序算法
    福利又来了,mongo还不会快来练起来!操作汇总
    Linux 操作另一台服务器
    Istio服务网格进阶⑦:Istio流量治理之故障注入与重试
    下一个行业风口:NFT 数字藏品,是机遇还是泡沫?
    Python绘图系统22:实现系统菜单
    Linuxptp安装部署
    微服务17:微服务治理之异常驱逐
  • 原文地址:https://blog.csdn.net/weixin_43312470/article/details/108045979