c++ 输入输出流

文章目录

  • 输入输出
    • 流的含义
  • 标准I/O
  • 文件I/O
    • 文件概念
    • 文件流对象
  • 字符串I/O

前言

前面所用到的输入和输出,都是以终端为对象的,即从键盘输入数据,运行结果输出
到显示器屏幕上。从操作系统的角度看,每个与主机相连的输入输出设备都看作一个
文件。例如.终端键盘是输入文件,显示屏和打印机是输出文件。除了以终端为对象进行
输入和输出外,还经常用磁盘或光盘作为输入输出对象,这时,磁盘文件既可以作为输入
文件,也可以作为输出文件。
说明:
C语言采用函数实现输入输出(如scanf和printf函数),c++采用类对象来实现输入输出(如cin和cout)。

输入输出

程序的输入指的是从输入文件将数据传送到内存单元,程序的输出指的是从程序把
内存单元中的数据传送给输出文件。C++的输入与输出包括以下3方面的内容:

(1)对系统指定的标准设备的输入和输出。即从键盘输入数据,输出到显示器屏幕。
这种输入输出称为标准的输入输出,简称标准I/O
(2)以外存(磁盘、光盘)为对象进行输入和输出, 例如从磁盘文件输入数据,数据输
出到磁盘文件。这种以外存文件为对象的输入输出称为文件的输入输出,简称文件I/O
(3)对内存中指定的空间进行输入和输出。通常指定一个字符数组作 为存储空间
(实际上可以利用该空间存储任何类型的信息)。这种输入和输出称为字符串输入输出,
简称串I/O

为了实现数据的有效流动,C++系统提供了庞大的I/O类库,调用不同的类去实现
不同的功能。

流的含义

C++的输入输出流
输入和输出是数据传送的过程,数据如流水一样从一处流向另一处。C++形象地将
此过程称为流( stream)。C++的输入输出流是指由若干字节组成的字节序列,这些
字节中的数据按顺序从一个对象传送到另一对象。 流表示了信息从源到目的端的流动。

在输入操作时,字节流从输入设备(如键盘、磁盘)流向内存,在输出操作时,字节流从内存流向输出设备(如屏幕、打印机、磁盘等)。流中的内容可以是ASCII字符二进制形式的数据、图形图像、数字音频视频或其他形式的信息。

流是与内存缓冲区
实际上,在内存中为每一个数据流开辟一个内存缓冲区,用来存放流中的数据。当用
cout和插入运算符“<<”向显示器输出数据时,先将这些数据插入到输出流(cout流),送
到输出缓冲区保存,直到缓冲区满了或遇到endl,就将缓冲区中的全部数据送到显示器显
示出来。在输入时,从键盘输入的数据先放在键盘的缓冲区中,当键入回车符时,键盘缓
冲区中的数据输入到计算机的输入缓冲区,形成cin流,然后用提取运算符“>>”从输入
缓冲区中提取数据送给程序中的有关变量。总之,流是与内存缓冲区相对应的,或者说,
缓冲区中的数据就是流。
在C++中,输入输出流被定义为类。C++的I/O库中的类称为流类,用流类定义的对象称为流对象。
c++ 输入输出流_第1张图片

标准I/O

C++的流类库中定义了四个全局流对象:cin,cout,cerr和clog。
cin标准输入流对象,键盘为其对应的标准设备;
cout标准输出流对象,显示器为标准设备。
cerr和clog标准错误输出流,输出设备是显示器。
在流类库中,最重要的两部分功能为标准输入/输出和文件处理。在新库中,要使用这四个功能,必须包含文件并引入std标准命名空间。

标准输入流cin值得注意的问题

流提取符">>"从流中提取数据时通常跳过输入流中的空格、tab键、换行符等空白字符。
注意:只有在键盘输入完数据并按回车键后,该行数据才能被送入键盘缓冲区,形成输入流,提取运算符才可以从中提取到数据。

标准输出流cout注意的问题
cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据,当向cout流插入一个endl时,不论缓冲区是否已满,都立即输出流中所有数据,然后插入一个换行符,并刷新流(清空缓冲区)。

文件I/O

文件概念

对用户来说,常用到的文件有两大类,一类是程序文件,如C ++的源程序文件(. cpp)、目标文件(. obj)、可执行文件(.exe)等。一类是数据文件,在程序运行时,常常需要将一些数据(运行的最终结果或中间数据)输出到磁盘上存放起来,以后需要时再从磁盘中输入到计算机内存,这种磁盘文件就是数据文件。程序中的输入和输出的对象就是数据文件。
根据文件中数据的组织形式,可分为ASCII文件和二进制文件

  • ASCII 文件又称文本文件或字符文件,文件中每一个字节放一个ASCI代码,代表一个字符。
  • 二进制文件又称内部格式文件或字节文件,如果把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放,就是二进制文件。

字符型数据在内存中是以ASCII代码形式存放的,因此,无论用ASCII文件输出还是用
二进制文件输出,其存储形式是一样的。但是对于数值型数据,二者是不同的。例如有一个
整数100000,而整数在内存中占4个字节,如果按内部格式直接输出,在磁盘文件中
也占4个字节,如果将它转换为ASCII码形式输出,6个字符便占6个字节。
用ASCII代码形式输出的数据是与字符一一对应的,一个字节中的ASCII码代表一个字符,可以对字符逐个进行输入输出,可以直接在屏幕上显示或打印出来。这种方式使
用方便,比较直观,但一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII
码间的转换)。用内部格式(二进制形式)输出数值,可以节省外存空间,而且不需要转换
时间,但一个字节并不对应一一个字符,不能直观地接显示文件中的内容。
总结
如果在程序运行过程中想把一些中间结果暂时保存在磁盘文中,以后需要时再输入到内存继续运算的,用二进制文件保存是最合适的。如果是为了能显示和打印以供阅读,则应按ASCII码形式输出。此时得到的是ASCII文件,它的内容可以直接在显示屏上观看。

文件流对象

文件流:文件流是以外存文件为输入输出对象的数据流。输出文件流是从内存流向外存文件的数据,输入文件流是从外存文件流向内存的数据。

文件的操作步骤:

  1. 定义一个文件流对象
    ifstream类 (从磁盘文件输入)
    ofstream类 (向磁盘文件输出)
    fstream类 (既磁盘文件的输入输出)
  2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
  3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
  4. 关闭文件

使用文件IO流用文本及二进制方式演示读写配置文件

#include
#include
#include
using namespace std;

struct ServerInfo //f服务器ip和端口号信息
{
	char _ip[32];
	int _port;
};


struct ConfigManager
{
public:
	ConfigManager(const char* configfile = "myserver.config")
		:_configfile(configfile)
		{}
	// 二进制读写  内存二进制是什么样,写到磁盘就是什么样 (缺点:文件打开以后是乱码)
	void WriteBin(const ServerInfo& info)
	{
		ofstream ofs(_configfile.c_str());//定义ofstream类(输出文件流类)对象ofs,打开磁盘文件“myserver.config”,只能向他写入数据,不能从中读取数据
		ofs.write((const char*)&info, sizeof(ServerInfo));//将ip与port信息写入磁盘文件myserver.config
		ofs.close();
	}
	void ReadBin(ServerInfo& info)
	{
		ifstream ifs(_configfile.c_str());//定义ifstream类(输入文件流类)对象ifs,打开磁盘文件
		ifs.read((char*)&info, sizeof(ServerInfo)); //把磁盘文件内容读入到内存
		ifs.close();
	}

	// 文本读写  转成字符串写出去。(优点:写出去方便读了,缺点:比较麻烦) 
	/* 传统的写法,转字符串写出,读字符串再转成要的类型对象  这个过程序列化和反序列化,类似于linux网络编程里的socket套接字编程里的网络字节序和主机字节序转换问题
	void WriteText(const ServerInfo& info)
	{
	ofstream ofs(_configfile.c_str());//定义输出文件流类对象,打开文件
	ofs.write(info._ip, strlen(info._ip));//字符串类型直接写入
	ofs.put('\n');//调用成员函数put写入单个字符 这里是换行符
	string ipstr = to_string(info._port);//整型转字符串
	ofs.write(ipstr.c_str(), ipstr.size());
	ofs.close();
	}

	void ReadText(ServerInfo& info)//读回去
	{
	ifstream ifs(_configfile.c_str());
	ifs.getline(info._ip, 32);//调用成员函数getline读取一行
	char ipstr[10];
	ifs.getline(ipstr, 10);
	info._port = stoi(ipstr);//字符串转整型
	ifs.close();
	}*/
	//现代写法
	void WriteText(const ServerInfo& info)
	{
		ofstream ofs(_configfile.c_str());
		ofs << info._ip << endl;//写入输出文件类流对象
		ofs << info._port << endl;   // 底层也是整形转成字符串写出的
		ofs.close();
	}

	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_configfile.c_str());
		ifs >> info._ip;
		ifs >> info._port;          // 底层读入字符串转成整形
		ofs,close();
	}

private:
	std::string _configfile; // 配置文件
};
int main()
{
	ServerInfo info;
	strcpy(info._ip, "192.168.1.1");
	info._port = 80;

	//二进制读写
	//ConfigManager cm;
	//cm.WriteBin(info);//将serverinfo信息写入磁盘文件
	//
	//ServerInfo ret;
	//cm.ReadBin(ret);//将磁盘文件内容读到内存变量中
	//cout << ret._ip << endl;//打印信息
	//cout << ret._port << endl;

	//文本读写
	ConfigManager cm;
	cm.WriteText(info);
	
	ServerInfo ret;
	cm.ReadText(ret);
	cout << ret._ip << endl;
	cout << ret._port << endl;

	return 0;
}

字符串I/O

字符串流
文件流是以外存文件为输入输出对象的数据流,字符串流不是以外存文件为输入输
出的对象,而以内存中用户定义的字符数组(字符串)为输入输出的对象,即将数据输出到内存中的字符数组,或者从字符数组(字符串)将数据读入。字符串流也称为内存流。

字符串流也有相应的缓冲区,开始时流缓冲区是空的。如果向字符数组存入数据,随
着向流插入数据,流缓冲区中的数据不断增加,待缓冲区满了(或遇换行符),一起存入字
符数组。如果是从字符数组读数据,先将字符数组中的数据送到流缓冲区,然后从缓冲区
中提取数据赋给有关变量。

在字符数组中可以存放字符,也可以存放整数、浮点数以及其他类型的数据。在向字
符数组存入数据之前,要先将数据从二进制形式转换为ASCII代码,然后存放在缓冲区,
再从缓冲区送到字符数组。从字符数组读数据时,先将字符数组中的ASCII数据送到缓
冲区,在赋给变量前要先将ASCI代码转换为二进制形式。总之,流缓冲区中的数据格
式与字符数组相同。这种情况与以标准设备(键盘和显示器)为对象的输入输出是类似
的,键盘和显示器都是按字符形式输入输出的设备,内存中的数据在输出到显示器之前,
先要转换为ASCII码形式并送到输出缓冲区中,从键盘输入的数据以ASCII码形式输入到输入缓冲区,在赋给变量之前转换成相应变量类型的二进制形式,然后赋值给变量。

//字符串io
#include

int main()
{
	int a = 1, b = 3;
	//cin >> a >> b;
	printf("Add(%d, %d)等于%d\n", a, b, a + b);
	cout << "Add(" << a << "," << b << ")" << "等于" << a + b << endl;//麻烦


	// sprintf格式化输入到字符数组中,缺陷是万一序列化出的字符串太长,buff就越界了
	char buff[128];
	sprintf(buff, "Add(%d, %d)等于%d", a, b, a + b);

	// stringstream流对象     优势:就是无论序列化出的字符串多长,都够用。  写起来没有上面那么方便直观。
	stringstream ss;
	ss << "Add(" << a <<","<< b << ")"<<"等于" << a + b;
	string str = ss.str();
	cout << str << endl;



	

	//	序列化与反序列化
	// 张三  18  1823123131
	// 发送端
	stringstream send; //建立输出字符串流对象
	send << "张三" << " " << 18 << " " << "1823123131"<<endl;//将数据插进去
	string str = send.str();

	// 接收端
	stringstream recv(str);//建立输入字符串流对象
	string name;
	int age;
	string tel;
	recv >> name >> age >> tel;//读入时和cin一样,以空格、换行符、tab键为界限

	cout << name << "-" << age << "-" << tel << endl;



	//字符串流对象常用用法
	string tmp("hello world who am i");
	stringstream ss(tmp);
	string word;
	while (ss >> word)//正常从ss流读取数据,值为真,执行循环体
	{
		cout << word;
	}
	return 0;
}

你可能感兴趣的:(c++)