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 <iostream>
#include <iomanip>
#include<string>
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 <iostream>
#include <iomanip>
#include<fstream>
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 <iostream>
#include <iomanip>
#include<fstream>
#include<strstream>
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;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43312470/article/details/108045979