目录
一、C++ 的 I/O 流
二、C++ 的标准 I/O 流
三、C++ 的文件 I/O 流
一、C++ 的 I/O 流
C 语言有一套完成数据读写(I/O)的解决方案:
-
使用 scanf()、gets() 等函数从键盘读取数据,使用 printf()、puts() 等函数向屏幕输出数据;
-
使用 fscanf()、fgets() 等函数从文件中读取数据,使用 fprintf()、fputs() 等函数向文件中写入数据。
C 语言的这套 I/O 解决方案也适用于 C++ 程序,但 C++ 独立开发了一套全新的 I/O 解决方案。
本质上来说,C++ 的这套 I/O 解决方案就是一个包含很多类的类库(作为 C++ 标准库的组成部分),这些类常被称为 "流类"。
C++ 的开发者认为数据输入和输出的过程也是数据传输的过程,数据像水一样从一个地方流动到另一个地方,所以 C++ 中将此过程称为 "流",实现此过程的类称为 "流类"。
下图展示了 C++ 中用于实现数据输入和输出的这些流类以及它们之间的关系:
注意:
-
图中的箭头代表各个类之间的派生关系。为了解决菱形继承中的问题,从 ios 派生出 istream 和 ostream 时,均使用了 virtual 关键字(虚继承)。
-
实现标准 I/O 操作的流类为 istream(从键盘读取数据)、ostream(向屏幕输出数据) 和 iostream(兼 istream 和 ostream 类功能于一身)。
-
实现文件 I/O 操作的流类为 ifstream(从文件中读取数据)、ofstream(向文件中写入数据) 和 fstream(兼 ifstream 和 ofstream 功能于一身)。
二、C++ 的标准 I/O 流
cin 是 istream 类对象;cout、cerror 和 clog 是 ostream 类对象。
注意:
-
cerr、clog 的用法和 cout 完全一样,但 cerror 常用来输出警告和错误信息给程序的使用者,clog 常用来输出程序执行过程中的日志信息(此部分信息只有程序开发者看得到,不需要对普通用户公开)。
-
cin 和 cout 可以直接输入和输出内置类型数据,因为标准库中已经将所有内置类型的输入和输出全部重载了。
-
对于自定义类型,如果要支持 cin 和 cout 的标准输入输出,需要对 << 和 >> 进行重载。
#include <iostream> using namespace std; class Date { friend istream& operator>>(istream& in, Date& d); friend ostream& operator<<(ostream& out, const Date& d); public: Date(int year = 1949, int month = 10, int day = 1) : _year(year), _month(month), _day(day) { } private: int _year; int _month; int _day; }; istream& operator>>(istream& in, Date& d) { return in >> d._year >> d._month >> d._day; } ostream& operator<<(ostream& out, const Date& d) { return out << d._year << "-" << d._month << "-" << d._day; } int main() { Date d; cin >> d; cout << d << endl; return 0; }
-
连续输入时,在 VS 中输入
Ctrl + z
结束。#include <iostream> using namespace std; int main() { string s; while (cin >> s) { cout << s << endl; } return 0; }
注意:
cin >> s
等价于operator>>(cin, s).operator bool()
。类型转换操作符(type conversion operator)是一种特殊的类成员函数,它定义了从类类型值到其他类型值的转换。
函数原型:
operator type() [const];
转换函数必须是成员函数,不能指定返回类型,并且形参列表必须为空。
三、C++ 的文件 I/O 流
根据数据的组织形式,文件可分为二进制文件和文本文件。
-
二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放,即存放的是数据的原形式。
-
文本文件是把数据的终端形式的二进制数据输出到磁盘上存放,即存放的是数据的终端形式。
采用文件流对象操作文件的一般步骤:
-
定义一个文件流对象;
-
使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系;
-
使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写;
-
关闭文件。
struct ServerInfo
{
char _address[32];
int _port;
Date _date;
};
class ConfigManager
{
public:
ConfigManager(const char* filename) : _filename(filename)
{ }
void WriteBin(const ServerInfo& info)
{
ofstream ofs(_filename, ios_base::out | ios_base::binary);
ofs.write((const char*)&info, sizeof(info));
}
void ReadBin(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
ifs.read((char*)&info, sizeof(info));
}
void WriteText(const ServerInfo& info)
{
ofstream ofs(_filename);
ofs << info._address << " " << info._port << " " << info._date;
}
void ReadText(ServerInfo& info)
{
ifstream ifs(_filename);
ifs >> info._address >> info._port >> info._date;
}
private:
string _filename; // 配置文件
};
int main()
{
ServerInfo info = { "192.0.0.1", 80, { 2023, 10, 1 } };
// 二进制读写
ConfigManager cm_bin("test.bin");
cm_bin.WriteBin(info);
ServerInfo info_bin;
cm_bin.ReadBin(info_bin);
cout << info_bin._address << " " << info_bin._port << " " << info_bin._date << endl;
// 文本读写
ConfigManager cm_text("text.txt");
cm_text.WriteText(info);
ServerInfo info_text;
cm_text.ReadText(info_text);
cout << info_text._address << " " << info_text._port << " " << info_text._date << endl;
return 0;
}