欢迎来到C++专栏~~IO流
(꒪ꇴ꒪(꒪ꇴ꒪ ),我是Scort目前状态:大三非科班啃C++中博客主页:张小姐的猫~江湖背景快上车,握好方向盘跟我有一起打天下嘞!送给自己的一句鸡汤樂:真正的大师永远怀着一颗学徒的心作者水平很有限,如果发现错误,可在评论区指正,感谢欢迎持续关注!
文章目录
欢迎来到C++专栏~~IO流一. C语言的输入与输出二. 流是什么三. C++IO流C++标准IO流✨C++文件IO流⚡以二进制的形式操作文件⚡以文本的形式操作文件⚡使用>>和<<对文件进行操作
四. stringstream的简单介绍
写在最后
一. C语言的输入与输出
C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()
scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)
C语言借助了相应的缓冲区来进行输入与输出
对输入输出缓冲区的理解:
可以屏蔽掉低级I/O的实现,低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序。可以使用这部分的内容实现“行”读取的行为,对于计算机而言是没有“行”这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一个“行”
二. 流是什么
“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且有方向性的数据的抽象描述。
C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”
流的特性:有序连续、具有方向性
三. C++IO流
C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类
从上图可以看出,cout、cerr、clog都是由ostream类实例化出的三个不同的对象,因此这三个对象基本没什么区别,只是应用场景不同。
C++标准IO流
C++标准库提供了4个全局流对象(cin、cout、cerr、clog):
使用cout进行标准输出,即数据从内存流向控制台(显示器)使用cin进行标准输入,即数据通过键盘输入到程序中使用cerr进行标准错误的输出使用clog进行日志的输出
注意: 1、在使用cin、cout时必须要包含iostream文件,并引入std标准命名空间。
#include
using namespace std; //引入std标准命名空间
int main()
{
int a = 0;
cin >> a;
cout << a << endl;
return 0;
}
或者指定cout和cin所属的命名空间
#include
int main()
{
int a = 0;
std::cin >> a; //使用时指定所属命名空间
std::cout << a << std::endl; //使用时指定所属命名空间
return 0;
}
2、cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中提取。如果一次输入过多,则多余的数据会留在缓冲区以供之后提取,如果输入错了,必须在回车之前进行修改,回车键按下就无法进行修改了,只有把输入缓冲区中的数据取完后,才会要求输入新的数据
例如,对于以下代码,若在第一次输入时便以空格为分隔输入了两个数据,则在下一次需要提取数据的时候就直接从缓冲区进行提取
#include
using namespace std;
int main()
{
int a = 0, b = 0;
cin >> a; //输入:10 20
cout << a << endl;
cin >> b; //直接从输入缓冲区提取
cout << b << endl;
return 0;
}
3、 输入的数据类型必须与要提取的数据类型一致,否则出错。出错只是在流的状态字state中对应位置位(置1),程序继续
4、空格和回车都可以作为数据之间的分隔符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格无法用cin输入,字符串中也不能有空格,回车符也无法读入。 例如,我们使用cin无法将含空格的字符串"hello world"输入到string对象中。
#include
#include
using namespace std;
int main()
{
string s;
cin >> s; //输入:"hello world"
cout << s << endl; //输出:"hello"
return 0;
}
对于含有空格的字符串,我们需要使用getline函数进行读取,因为getline函数只有遇到’\n’才会停止读取。
#include
#include
using namespace std;
int main()
{
string s;
getline(cin, s); //输入:"hello world"
cout << s << endl; //输出:"hello world"
return 0;
}
5、cin和cout可以直接输入和输出内置类型的数据。因为标准库已经将所有内置类型的输入和输出进行了重载。
>>的运算符的重载: <<的运算符的重载:
6、对于自定义类型,如果要支持cin和cout的标准输入输出,则需要对<<和>>进行重载。 例如,对于下面简单实现的日期类,在对<<和>>进行重载后就能够支持cin和cout的输入输出了。
#include
using namespace std;
class Date
{
friend istream& operator>>(istream& in, Date& d);
friend ostream& operator<<(ostream& out, const Date& d);
public:
Date(int year = 2021, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
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()
{
Date d;
cin >> d; //输入:2021 9 20
cout << d << endl; //输出:2021-9-20
return 0;
}
7、输入多个值,默认就是用空格或者换行分割的
int year, month, day;
cin >> year >> month >> day;
scanf("%d%d%d", &year, &month, &day);
//scanf("%d %d %d", &year, &month, &day);//不需要去加空格
如果输入的数值严格是20221128,可以用substr(),进行断句
//20230219
string str;
cin >> str;
year = stoi(str.substr(0, 4));
month = stoi(str.substr(4, 2));
day = stoi(str.substr(6, 2));
8、在线OJ中的输入和输出
某些在线OJ题目是有多组测试用例对我们编写的代码进行测试的,对于这种题目,C语言通常采用的解决方式如下:
while (scanf("%d", &a) != EOF)
{
//...
}
而在C++当中,我们一般采用的解决方式如下:
while (cin >> a)
{
//...
}
C++中是运用了operator bool
✨C++文件IO流
C++根据文件内容的数据格式将文件分为二进制文件和文本文件,采用文件流对象操作文件的一般步骤如下: 1、定义一个文件流对象。 操作文件的类有以下三个:
类对应操作场景ofstream只写ifstream只读fstream读和写
2、文件常见的打开方式如下:
打开方式功能in以读的方式打开文件out以写的方式打开文件binary以二进制方式对文件进行操作ate输出位置从文件的末尾开始app以追加的方式对文件进行写入trunc先将文件内容清空再打开文件
3、对文件进行提取和插入操作的常用成员函数:
成员函数功能put插入一个字符到文件write插入一段字符到文件get从文件提取字符readreadtellg获取当前字符在文件当中的位置seekg设置对文件进行操作的位置>>运算符重载将数据形象地以“流”的形式进行输入<<运算符重载将数据形象地以“流”的形式进行输出
3、关闭文件
⚡以二进制的形式操作文件
二进制的读写:在内存中如何存储,就如何写到磁盘文件中 优点:快 缺点:写出去的内容看不见
以二进制的形式对文件进行写入操作:
//配置文件
struct ServerInfo
{
char _address[32]; //ip
int _port; //端口
};
//配置管理器
struct ConfigManager
{
public:
ConfigManager(const char* filename = "test.txt")
:_filename(filename)
{}
//写
void WriteBin(const ServerInfo& info)
{
ofstream ofs(_filename, ios_base::out | ios_base::binary);
ofs.write((char*)&info, sizeof(info));
}
//读
void ReadBin(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
ifs.read((char*)&info, sizeof(info));
}
private:
string _filename; // 配置文件
};
int main()
{
//二进制写
ServerInfo winfo = { "192.0.0.1", 12138 };
ConfigManager cm;
cm.WriteBin(winfo);
//二进制读
ServerInfo rinfo;
ConfigManager cm;
cm.ReadBin(rinfo);
cout << rinfo._address << endl;
cout << rinfo._port << endl;
}
此处要注意:
struct ServerInfo
{
//char _address[32]; //ip
string _address; //此处用string会更好
int _port; //端口
};
但会引发一个问题:打印出来的是乱码 其原因是:数据存在堆中,地址会更改
所以二进制读写的时候不适合用string,更多用的是数组来解决
文本读写就不限制,二者都可以用
⚡以文本的形式操作文件
文本读写:对象数据序列化字符串写出来,堵回来也是:字符串反序列化转成对象数据
优点:可以看得见写出去的是什么 缺点:存在一个转换的过程
//文本写
void WriteText(const ServerInfo& info)
{
//c_str() : 将C++的string转化为C的字符串数组,c_str()生成一个const char *指针,指向字符串的首地址
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(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
char buff[128];
ifs.getline(buff, 128);
info._address = buff;
ifs.getline(buff, 128);
info._port = stoi(buff);
}
int main()
{
//文本写
ServerInfo winfo = { "192.0.0.1", 111111};
ConfigManager cm;
cm.WriteText(winfo);
//文本读
ServerInfo rinfo;
ConfigManager cm;
cm.ReadText(rinfo);
cout << rinfo._address << endl;
cout << rinfo._port << endl;
}
⚡使用>>和<<对文件进行操作
使用>>和<<运算符对文件进行读写操作,会变得很简单,也很形象 如果自定义类型也支持流插入、提取的重载
//对文件进行写入操作
void WriteTest(const ServerInfo& info)
{
ofstream ofs(_filename, ios_base::out);
ofs << info._address < ofs << info._port < ods << info._Date < } 对文件的读取: //对文件进行读取操作 void ReadFile() { ifstream ifs("data.txt"); //定义文件流对象,并打开文件 char data[100]; ifs >> data; //文件数据“流入”字符串data ifs.close(); //关闭文件 } 四. stringstream的简单介绍 在C语言中,我们若是想要将一个整型变量的数据转化为字符串格式,有以下两种方法: 使用itoa函数进行转化 int a = 10; char arr[10]; itoa(a, arr, 10); //将整型的a转化为十进制字符数字存储在字符串arr当中 使用sprintf函数进行转化 int a = 10; char arr[10]; sprintf(arr, "%d", a); //将整型的a转化为字符串格式存储在字符串arr当中 虽然itoa函数和sprintf函数都能完成转化,但是在两个函数在转化时,都需要先给出保存结果的空间,而空间的大小是不太好界定的,除此之外,转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃。 在C++中,我们可以使用stringstream类对象来避开此问题。在程序当中如果想要使用stringstream,必须要包含头文件sstream。在该头文件下,有三个类: 此处重点讲解:序列化和反序列化结构数据 struct ChatInfo { string _name; // 名字 int _id; // id }; int main() { // 结构信息序列化为字符串 ChatInfo winfo = { "张三", 135246}; ostringstream oss; oss << winfo._id << endl; oss << winfo._name << endl; string str = oss.str(); cout << str << endl; //网络传输str ~ //反系列化 ChatInfo rInfo; istringstream iss(str); iss >> rInfo._id; iss >> rInfo._name; cout << "-------------------------------------------------------"<< endl; cout << "姓名:" << rInfo._name << "(" << rInfo._id << ") "<< endl; cout << "-------------------------------------------------------"<< endl; return 0; } 写在最后 好文阅读
发表评论