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){}// 重载booloperator bool(){// 这里是随意写的,假设输入_year为0,则结束if (_year == 0)return false;elsereturn 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流
std::ofstream::open
二进制读写
#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;elsereturn 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;elsereturn 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;elsereturn 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; // idDate _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;
}