C++知识点 – C++的IO流
文章目录
- C++知识点 -- C++的IO流
- 一、C++标准IO流
- 1.多个输入数据
- 2.多行数据读入
- 二、C++文件IO流
- 1.ifstream读文件
- 2.文件读写类
- 三、stringstream
一、C++标准IO流
C++系统实现了一个庞大的类库,其中以ios为基类,其他类都是直接或间接派生自ios类;
C++标准IO流提供了4个全局流对象,cin、cout、cerr、clog,其中最常用的就是cin和cout;
ostream/istream 更好支持自定义类型对象的流插入和流提取;
自定义类型,可以自己重载,控制流提取和流插入的方式;
1.多个输入数据
void test()
{
int year, month, day;
cin >> year >> month >> day;
scanf("%d%d%d", &year, &month, &day);
//scanf("%d %d %d", &year, &month, &day);//中间不需要加空格
}
cin和scanf输入多个值都是默认使用空格或者换行符分割的,中间不用加空格;
如果是20230112这种格式的输入:
void test()
{
//如果是20230112这种格式
int year, month, day;
scanf("%4d%2d%2d", &year, &month, &day);//scanf可以用这种方式
//c++就需要使用string的字符分割
string str;
cin >> str;
year = stoi(str.substr(0, 4));
month = stoi(str.substr(4, 2));
day = stoi(str.substr(6, 2));
}
2.多行数据读入
c语言可以使用scanf() != EOF来读取;
c++的读取方式如下:
void test()
{
int year, month, day;
string str;
while (cin >> str)
{
year = stoi(str.substr(0, 4));
month = stoi(str.substr(4, 2));
day = stoi(str.substr(6, 2));
}
}
结束程序可以用ctrl + Z + 换行 或者 ctrl + c
原因是cin读取数据后还会返回bool值,cin返回布尔值是通过重载operator bool实现的;
当读到文件末尾或者错误时,会返回false;
这里涉及到隐式类型转换:
class A
{
public:
A(int a)
: _a(a)
{}
operator int()//operator int支持了自定义类型转换成内置类型
{
return _a;
}
private:
int _a;
};
void test()
{
//内置类型 转换为 自定义类型
A a1 = 1; // 隐式类型转换, 先用1构造A临时对象,再拷贝构造,优化后直接调用构造
//自定义类型 转换为 内置类型
int i = a1; // 原本编译器是不支持的,但是A中重载了operator int 后,就支持自定义类型转换成内置类型
}
二、C++文件IO流
C++文件IO流中包含三个对象:
ifstream:只输入用
ofstream:只输出用
fstream:既能输入又能输出
1.ifstream读文件
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;
}
void test()
{
ifstream ifs("text.txt");//可以直接用文件路径构造ifstream对象,就直接打开文件了
//ifstream ifs();
//ifs.open("text.txt");//也可以构造空对象,再调用fopen打开文件
int i;
string str1, str2;
Date d1;
ifs >> i >> str1 >> d1 >> str2;
}
构造ifstream对象之后就可以打开文件进行读取了,ifstream对象支持流提取,从文件中直接提取对象;
使用ifstream的流提取,是继承于istream的;
这里c++的流提取支持直接提取到自定义类型(前提是自定义类型重载了流提取);
2.文件读写类
二进制读写文件
struct SeverInfo
{
char _address[32];
int _port;
};
class ConfigManager
{
public:
ConfigManager(string filename = "sever.config")
: _filename(filename)
{}
//二进制写
void WriteBin(const SeverInfo* info)
{
ofstream ofs(_filename, ios_base::out | ios_base::binary);//二进制写
ofs.write((char*)info, sizeof(SeverInfo));
}
//二进制读
void ReadBin(SeverInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary); //二进制读
ifs.read((char*)&info, sizeof(SeverInfo));
}
private:
string _filename;
};
void test()
{
//二进制写文件
SeverInfo winfo = { "127.0.1", 888 };
ConfigManager cm;
cm.WriteBin(&winfo);
//二进制读文件
SeverInfo rinfo;
cm.ReadBin(rinfo);
cout << rinfo._address << endl;
cout << rinfo._port << endl;
}
二进制读写时,ifstream和ofstream对象的第二个参数应该使用in/out和binary的组合,多个状态组合中间用 | 分割;
如果将SeverInfo中的address从char* 换成string,就会出错,关系到c++的string的设计问题;
长字符串是存在指针处的,写完之后进程结束,指针释放,读的时候下一个进程给的ptr不是一个地址,就会出现读取错误;
所以说二进制不适合深拷贝的类型;
文本读写文件
struct SeverInfo
{
//char _address[32];
string _address;
int _port;
};
class ConfigManager
{
public:
ConfigManager(string filename = "sever.config")
: _filename(filename)
{}
//文本写
void WriteText(const SeverInfo* info)
{
ofstream ofs(_filename, ios_base::out);//二进制写
ofs.write(info->_address.c_str(), info->_address.size());
ofs.put('\n');
const string str = to_string(info->_port);
ofs.write(str.c_str(), str.size());
}
//文本读
void ReadText(SeverInfo& info)
{
ifstream ifs(_filename, ios_base::in); //二进制读
char buff[128];
ifs.getline(buff, 128);
info._address = buff;
ifs.getline(buff, 128);
info._port = stoi(buff);
}
private:
string _filename;
};
这是使用write和read进行读写,更加方便地方式是使用流插入和流提取进行读写:
struct SeverInfo
{
//char _address[32];
string _address;
int _port;
};
class ConfigManager
{
public:
ConfigManager(string filename = "sever.config")
: _filename(filename)
{}
void WriteText(const SeverInfo& info)
{
ofstream ofs(_filename, ios_base::out);
ofs << info._address << endl;
ofs << info._port << endl;
}
void ReadText(SeverInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
ifs >> info._address >> info._port;
}
private:
string _filename;
};
三、stringstream
字符串流:完成字符串和其他类型之间的转换;
将提取到的信息转换为字符串存储到流中:
void test()
{
// 序列化
ChatInfo winfo = { "张三", 135246, { 2022, 4, 10 }, "晚上一起看电影吧" };
//ostringstream oss;
stringstream oss; // stringstream既有ostringstream的功能,也有istringstream的功能
oss << winfo._name << endl;
oss << winfo._id << endl;
oss << winfo._date << endl;
oss << winfo._msg << endl;
//可以通过oss.str拿到流中的字符串
string str = oss.str();
cout << str << endl;
}
将流中的字符串转为其他类型
void test()
{
// 序列化
ChatInfo winfo = { "张三", 135246, { 2022, 4, 10 }, "晚上一起看电影吧" };
//ostringstream oss;
stringstream oss;
oss << winfo._name << endl;
oss << winfo._id << endl;
oss << winfo._date << endl;
oss << winfo._msg << endl;
string str = oss.str();
cout << str << endl;
// 网络传输str,另一端接收到了字符串串信息数据
// 反序列化
ChatInfo rInfo;
//istringstream iss(str);
//使用提取到的str来构造strinfstream对象,拿到字符串
stringstream iss(str);
iss >> rInfo._name;
iss >> rInfo._id;
iss >> rInfo._date;
iss >> rInfo._msg;
cout << "----------------------------------" << endl;
cout << rInfo._date << endl;
cout << rInfo._name << "[" << rInfo._id << "]:>" << rInfo._msg << endl;
cout << "----------------------------------" << endl;
}
stringstream的优势在于可以对自定义类型进行处理;