1. C语言的输入与输出
C语言中我们用到的最频繁的输入输出方式就是scanf ()
与printf()
。 scanf():
从标准输入设备(键 盘)读取数据,并将值存放在变量中。printf():
将指定的文字/字符串输出到标准输出设备(屏幕)。注意宽度输出和精度输出控制。C语言借助了相应的缓冲区来进行输入与输出。C语言借助了相应的缓冲区来进行输入与输出。如下图所示:
2. C++IO流
C++系统实现了一个庞大的类库,其中ios
为基类,其他类都是直接或间接派生自ios
类
#include<iostream>
using namespace std;
int main()
{
string str;
// 判断是否为真
// 方法1(整型判断):0为假,非0为真
// 方法2(指针判断):nullptr为假,非空为真
// str的类型为string,cin >> str其实是重载了operator>>,即
// istream& operator>> (istream& is, string& str);
// cin的类型为istream
// cin再通过隐式类型转换将istream转换为整型,或者指针,这样就可以判断其为真还是假了
while (cin >> str)
{
cout << str << endl;
}
}
- 原理
演示代码1
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
:_a1(1)
,_a2(2)
{}
// 通过这个重载,将自定义类型隐式转换为内置类型(重载int)
operator int()
{
return _a1 + _a2;
}
private:
int _a1;
int _a2;
};
int main()
{
// 内置类型隐式类型转换成自定义类型
A aa1 = 1;
// 自定义类型隐式类型转换成内置类型(这里调用了operator int();)
int a = aa1;
cout << a << endl;
}
这段代码演示了自定义类型
A
中的隐式类型转换,以及如何通过重载类型转换操作符operator int()
实现将自定义类型转换为内置类型int
。代码解释
自定义类
A
:
- 类
A
包含一个构造函数和两个私有成员变量_a1
和_a2
。- 没有显式定义类型转换构造函数,但是定义了一个将
A
类型转换为int
类型的类型转换函数operator int()
。重载类型转换操作符
operator int()
:
- 在类
A
中定义了将A
类型转换为int
类型的隐式类型转换操作符。- 当类
A
的对象被隐式转换为int
类型时,将返回_a1 + _a2
的结果。主函数中的隐式类型转换:
A aa1 = 1;
:这里使用int
类型的整数 1 初始化了类A
的对象aa1
。由于定义了构造函数A(int a)
,因此发生了从内置类型到自定义类型的隐式类型转换。int a = aa1;
:这里将类A
的对象aa1
隐式转换为int
类型,通过重载的类型转换操作符operator int()
,返回_a1 + _a2
的结果。输出结果:
cout << a << endl;
:打印变量a
,其值为_a1 + _a2
的结果,即 1 + 2,输出结果为3
。示例运行结果
3
通过这种方式,可以实现自定义类型到内置类型的隐式类型转换,方便地将自定义类型对象用于与内置类型相关的操作。但需要注意,过度使用隐式类型转换可能会导致代码可读性下降,应谨慎使用。
演示代码2
#include<iostream>
using namespace std;
class Date
{
friend ostream& operator << (ostream& out, const Date& d);
friend istream& operator >> (istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
// 重载bool
operator bool()
{
// 这里是随意写的,假设输入_year为0,则结束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >> (istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
ostream& operator << (ostream& out, const Date& d)
{
out << d._year << " " << d._month << " " << d._day;
return out;
}
// C++ IO流,使用面向对象+运算符重载的方式
// 能更好的兼容自定义类型,流插入和流提取
int main()
{
// 自动识别类型的本质--函数重载
// 内置类型可以直接使用--因为库里面ostream类型已经实现了
int i = 1;
double j = 2.2;
cout << i << endl;
cout << j << endl;
// 自定义类型则需要我们自己重载<< 和 >>
Date d(2022, 4, 10);
cout << d;
// 日期类作为循环条件,因此需要判断日期类为真或者假
while (d)
{
cin >> d;
cout << d;
}
return 0;
}
3.C++文件IO流
二进制读写
#include<iostream>
#include<fstream>
using namespace std;
// 二进制读写(不可以使用string)
struct ServerInfo
{
char _address[32];
int _port;
};
struct ConfigManager
{
public:
// 构造函数,私有成员变量为文件的文件名
ConfigManager(const char* filename)
:_filename(filename)
{}
// 将数据写入到文件中
void WriteBin(const ServerInfo& info)
{
// ios_base是ofstream的基类,因此两个类域都是可以用的,效果是一致的
// ofstream::out,流输出,ofstream::binary 二进制输入,输出
// ofstream ofs(_filename, ofstream::out | ofstream::binary);
ofstream ofs(_filename, ios_base::out | ios_base::binary);
// 将info指向的n个字符写入到文件中(也就是输出到文件中,因此使用的是ofstream)
ofs.write((char*)&info, sizeof(info));
}
// 从文件中读取数据
void ReadBin(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
// 从流中读取n个字符,并将其存储到info中
ifs.read((char*)&info, sizeof(info));
}
private:
string _filename; // 配置文件
};
int main()
{
ConfigManager cm("test.txt");
ServerInfo winfo = { "192.0.0.111111111111111111", 80};
// 写入到文件
// cm.WriteBin(winfo);
ServerInfo rinfo;
// 从文件中进行读取
cm.ReadBin(rinfo);
cout << rinfo._address << endl;
cout << rinfo._port << endl;
return 0;
}
- 错误演示(string)
#include<iostream>
#include<fstream>
using namespace std;
// 二进制读写
struct ServerInfo
{
string _address;
int _port;
};
struct ConfigManager
{
public:
ConfigManager(const char* filename)
:_filename(filename)
{}
// 将数据写入到文件中
void WriteBin(const ServerInfo& info)
{
// ios_base是ofstream的基类,因此两个类域都是可以用的,效果是一致的
// ofstream::out,流输出,ofstream::binary 二进制输入,输出
//ofstream ofs(_filename, ofstream::out | ofstream::binary);
ofstream ofs(_filename, ios_base::out | ios_base::binary);
// 将info指向的n个字符写入到文件中(也就是输出到文件中,因此使用的是ofstream)
ofs.write((char*)&info, sizeof(info));
}
// 从文件中读取数据
void ReadBin(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
// 从流中读取n个字符,并将其存储到info中
ifs.read((char*)&info, sizeof(info));
}
private:
string _filename; // 配置文件
};
- 情况1
// 情况1:一个进程中,同时读写,_ptr的地址相同
int main()
{
ConfigManager cm("test.txt");
ServerInfo winfo = { "192.0.0.111111111111111111", 80};
// 写入到文件
cm.WriteBin(winfo);
ServerInfo rinfo;
// 从文件中进行读取
cm.ReadBin(rinfo);
cout << rinfo._address << endl;
cout << rinfo._port << endl;
return 0;
}
- 情况2
// 情况2:两个进程,先写后读,_ptr变为野指针
// 先写
int main()
{
ConfigManager cm("test.txt");
ServerInfo winfo = { "192.0.0.111111111111111111", 80};
// 写入到文件
cm.WriteBin(winfo);
return 0;
}
// 后读
int main()
{
ConfigManager cm("test.txt");
ServerInfo rinfo;
// 从文件中进行读取
cm.ReadBin(rinfo);
cout << rinfo._address << endl;
cout << rinfo._port << endl;
return 0;
}
文本读写
#include<iostream>
#include<fstream>
using namespace std;
// 日期类
class Date
{
friend ostream& operator << (ostream& out, const Date& d);
friend istream& operator >> (istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{
// 这里是随意写的,假设输入_year为0,则结束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >> (istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
ostream& operator << (ostream& out, const Date& d)
{
out << d._year << " " << d._month << " " << d._day;
return out;
}
// 文本读写
struct ServerInfo
{
// char和string都可以使用,对于文本读写
// char _address[32];
string _address;
int _port;
Date _date;
};
struct ConfigManager
{
public:
ConfigManager(const char* filename)
:_filename(filename)
{}
void WriteText(const ServerInfo& info)
{
ofstream ofs(_filename);
// 输入换行,作为字符串的分隔符
ofs << info._address << endl;
ofs << info._port << endl;
ofs << info._date << endl;
}
void ReadText(ServerInfo& info)
{
ifstream ifs(_filename);
ifs >> info._address;
ifs >> info._port;
// 因为Date已经重载了流插入和流提取,因此可以直接以流插入的方式进行写入
// ifstream是istream的子类(派生类)
ifs >> info._date;
}
private:
string _filename; // 配置文件
};
int main()
{
ConfigManager cm("test.txt");
ServerInfo winfo = { "192.0.0.111111111111111111", 80, {2023,4,1} };
// 文件写入
cm.WriteText(winfo);
// 文件读取
ServerInfo rinfo;
cm.ReadText(rinfo);
cout << rinfo._address << endl;
cout << rinfo._port << endl;
cout << rinfo._date << endl;
return 0;
}
4.stringstream(万物皆可转字符串)
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;
class Date
{
friend ostream& operator << (ostream& out, const Date& d);
friend istream& operator >> (istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{
// 这里是随意写的,假设输入_year为0,则结束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >> (istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
ostream& operator << (ostream& out, const Date& d)
{
out << d._year << " " << d._month << " " << d._day;
return out;
}
int main()
{
int i = 1234;
double dl = 2.22;
Date d = { 2023, 4, 1 };
// 输出到流oss中
ostringstream oss;
oss << i << " "; // 使用空格分割字符串
oss << dl << " ";
oss << d << " ";
string str = oss.str();
cout << str << endl;
int j;
double dll;
Date dd;
// 从流iss中进行提取
istringstream iss(str);
iss >> j >> dll >> dd;
return 0;
}
序列化和反序列化
#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;
class Date
{
friend ostream& operator << (ostream& out, const Date& d);
friend istream& operator >> (istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{
// 这里是随意写的,假设输入_year为0,则结束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >> (istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
ostream& operator << (ostream& out, const Date& d)
{
out << d._year << " " << d._month << " " << d._day;
return out;
}
// 序列化和反序列化
struct ChatInfo
{
string _name; // 名字
int _id; // id
Date _date; // 时间
string _msg; // 聊天信息
};
int main()
{
// stringstream是ostringstream和istringstream的继承,因此其具有他们两个的功能
// 序列化
ChatInfo winfo = { "张三", 135246, { 2023, 4, 1 }, "晚上一起看电影吧" };
stringstream oss;
oss << winfo._name << endl;
oss << winfo._id << endl;
oss << winfo._date << endl;
oss << winfo._msg << endl;
// oss.str() 转化为c字符串
string str = oss.str();
cout << "网络发送:" << str << endl;
// 反序列化
ChatInfo rInfo;
stringstream iss(str);
iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg;
cout << "-------------------------------------------------------" << endl;
cout << "姓名:" << rInfo._name << "(" << rInfo._id << ") ";
cout << rInfo._date << endl;
cout << rInfo._name << ":>" << rInfo._msg << endl;
cout << "-------------------------------------------------------" << endl;
return 0;
}