C++练级计划-> 《IO流》iostream fstream sstream详解
如果是想全部过一遍就看完,如果想具体的了解某一个请点目录。因为有三种流的使用可能内容多
目录
流是什么?
C++IO流(iostream)
io流的注意事项
cin和cout为什么能直接识别出类型和数据
fstream
fstream的使用方法:
1.以二进制打开文件并写入和读取
2.以文本打开文件并读取或写入
3.用>> << 重载符号读取或写入
sstream
stringstream:
1.提取stringstream流中的数据
2.删除stringstream流中的数据
3.stringstream的常见用法:
4.stringstream能使用的oj题:
流是什么?
首先io流就是iostream,全称 in out stream,输入输出流。
怎么理解流呢?
把它想成一个数据结构(抽象的),里面流动的(存的)是字节流,或者字符流。
字节流就是二进制数据,字符流就是字符数据。二进制数据主要是图片视频等,字符数据就是一个文档全是字的。
所以流就可以想象成从一个地方拿数据,然后供给另一个地方,南水北调,南边的水,供给北方
这里中间传输的过程就是流。南水放入,北方取出加工使用。
C++IO流
这里的ios_base就是大基类(父类),里面保存的就是每个流都应该有的功能,ios是继承自ios_base的一个类,但是不只是只有一个ios,这里的ios类旨在处理char单字符,而对于wchar这样的宽字符它是无法处理的,所以还有另外一个wios类专门用于处理wchar。
ios就是后面的所有类的基类(父类)
iostream(经常用于屏幕打印数据)
in out stream
cin:流提取(istream),用于从io流中提取数据,比如说读文件、获取键盘的信息等
cout:流插入(ostream),将数据放入io流中,供其他程序提取。比如写文件(将数据写到屏幕上就是打印)
cerr:也是流插入(ostream)。某处出错时,将错误数据放入io流。一般在屏幕打印
clog:也是流插入(ostream)。进行日志的输出,屏幕打印
fstream(文件)
file stream:顾名思义这个流是给文件使用的。
sstream(字符串)
string stream:这个则是给字符串使用的。
io流的注意事项
注意:1.cin是以空格和换行为分隔符的
在使用cin时如果我们在输入时,数据中间加了空格,有人要提取数据,提取到的是空格前的数据。下一次有人又来提取时,这时是先把之前空格后的数据先给出去。
#include <iostream>
using namespace std;
int main()
{
int a = 0, b = 0;
cin >> a; //输入:10 20
cout << a << endl;
cin >> b; //直接从输入缓冲区提取
cout << b << endl;
return 0;
}
如上代码我们输入时输入 10 20,第二次cin时根本不会停下来等你输入数据,而是直接把20给b。然后输出。所以在使用cin时要注意空格的使用
2.输入类型和要输出到的类型必须一致
3.getline函数:用于输入时只把换行当做分隔符。
这个函数只能用于字符串,所以1.中输入数字的情况用不了
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
//cin>>s; //cin输入,最终只输出hello
getline(cin, s); //输入:"hello world"
cout << s << endl; //输出:"hello world"
return 0;
}
cin和cout为什么能直接识别出类型和数据
cin重载的>>运算符
cout重载的<<运算符
在C++标准库中对于内置类型的输入和输出已经全部重载,所以cin和cout能识别类型是这个原因。对于自定义类型而言: class类,在定义类时如果需要使用>> 和 << 需要自己手写接口实现。实现方法对应自己类的功能。
fstream
fstream有三个类和一个filebuf缓冲区
文件流的三个类:
类 | 对应操作场景 |
---|---|
ofstream | 只写 |
ifstream | 只读 |
fstream | 读+写 |
文件的打开方式:
打开方式 | 功能 |
---|---|
in | 以读的方式打开文件 |
out | 以写的方式打开文件 |
binary | 以二进制方式对文件进行操作 |
ate | 输出位置从文件的末尾开始 |
app | 以追加的方式对文件进行写入 |
trunc | 先将文件内容清空再打开文件 |
文件流的函数:
成员函数 | 功能 |
---|---|
put | 插入一个字符到文件 |
write | 插入一段字符到文件 |
get | 从文件提取字符 |
read | 从文件提取多个字符 |
tellg | 获取当前字符在文件当中的位置 |
seekg | 设置对文件进行操作的位置 |
>>运算符重载 | 将数据形象地以“流”的形式进行输入 |
<<运算符重载 | 将数据形象地以“流”的形式进行输出 |
fstream的使用方法:
1.以二进制打开文件并写入和读取
写入文件:
#include <iostream>
#include<fstream>
using namespace std;
void ofsteam_test_binary()
{
//为什么o是写入,output是输出的意思,意味着我们要从流中输出给文件
ofstream ofs;
ofs.open("binoptest.txt", ofstream::binary | ofstream::out);//以二进制写的方式打开文件
char str[] = "中国无敌";
ofs.write(str, strlen(str));
ofs.put('!');//put是单字符的写入
//使用任何类时都要记得close
ofs.close();
}
int main()
{
ofsteam_test_binary();
return 0;
}
解释:我们创建一个ofstream对象,用二进制out的方式打开文件binoptest.txt,为什么out是输出这里我们要把视角看成是流的视角:流把数据output进了binoptest.txt,所以ofstream是输出流。
结果:
1.在当前目录生成一个binoptest.txt文件
2.文件内容为:
读取文件:
void ifstream_test_binary()
{
ifstream ifs;
ifs.open("binoptest.txt", std::ios::binary | std::ios::in);
char str[32];
//计算文档内容的大小
ifs.seekg(0,std::ios::end);
int len = ifs.tellg();
ifs.seekg(0,std::ios::beg);
//读取文件放入str
ifs.read(str, len);
ifs.close();
//字符串末尾置为0
str[len] = '\0';
printf("%s", str);
}
解释:创建ifstream对象,打开文件创建str储存读取出来的数据。
注意:计算文档大小没有什么size函数但是可以用下面的代码
//计算文档内容的大小
ifs.seekg(0,std::ios::end);
int len = ifs.tellg();
ifs.seekg(0,std::ios::beg);
seekg用来定位读指针,第一个参数是偏移量,代表从哪里开始定位,第二个参数是根据偏移量后从哪里开始end代表结尾,beg代表开始,我们一开始就把定位移到了最后,然后tellg是用来计算当前读指针的偏移量的(从头开始计算),然后再让读指针回到beg开始,就完成了一次文档计数
结果 :
2.以文本打开文件并读取或写入
写入文件:
void ofstream_test_text()
{
ofstream ofs;
//默认的output方式是文本的
ofs.open("text.txt");
char str[] = "中国无敌";
ofs.write(str, strlen(str));
ofs.put('!');//put是单字符的写入
//使用任何类时都要记得close
ofs.close();
}
和二进制的区别只是,在open时二进制文件和文本文件不同,open默认打开文件是以文本方式打开的,如果要使用二进制打开就需要设置打开方式。
结果:
读取文件:
void ifstream_test_text()
{
ifstream ifs;
ifs.open("text.txt");
char str[32];
//计算文档内容的大小
ifs.seekg(0, std::ios::end);
int len = ifs.tellg();
ifs.seekg(0, std::ios::beg);
//读取文件放入str
ifs.read(str, len);
ifs.close();
//字符串末尾置为0
str[len] = '\0';
printf("%s", str);
}
和写入文件一样:和二进制的区别就是open的打开方式。
结果:
注意:除了指定二进制外,我们还可以指定读和写:默认的情况下:ifstream打开文件是以读的方式,ofstream打开文件是以写的方式打开,而fstream是以读加写的方式打开。
3.用>> << 重载符号读取或写入
写入文件:
void writefile()
{
ofstream ofs;
ofs.open("file.text");
string s = "中国无敌!";
ofs << s;
}
结果:
读取文件:
void readfile()
{
ifstream ifs;
ifs.open("file.text");
string s;
ifs >> s;
cout << s;
ifs.close();
}
结果:
可以看出如果以重载符来写入或者读取时使用方法和cout 和 cin 类似区别只是在于 cout 和 cin的默认设备分别是屏幕和键盘,而我们用文件流的写入和读取,可以自己选择文件。键盘和屏幕也是文件。
sstream
类 | 对应操作场景 |
---|---|
ostringstream | 输出操作 |
istringstream | 输入操作 |
stringstream | 输入操作+输出操作 |
介绍一下:sstream 全称 stringstream 字符串流:顾名思义一定和字符串有关,而这里的stringstream就是用来解决字符串转换的问题,在c语言时要进行字符串转换就要用到itoa等函数,在c++中用stringstream就可以完成
stringstream:
因为用法类似所以这里只介绍stringstream,stringstream是在底层维护了一个字符串,在这个字符串中完成对应的操作,放进这个字符串的数据默认就变成字符了。
1.提取stringstream流中的数据
有两种从流中提取数据的方法:
s.str(""); //将stringstream底层管理的string对象设置为""。
s.clear(); //将上次转换状态清空掉
1.使用>>从流中提取
int a = 10;
string sa;
stringstream s;
s << a; //将int类型的a放入输入流
s >> sa; //从s中抽取前面插入的int类型的值,赋值给string类型(方式一)
cout << sa << endl;
2.使用.str()函数的方式提取
double b = 3.14;
s << b;
sa = s.str(); //获取stringstream中管理的string类型(方式二)
cout << sa << endl;
二者没什么差别>>是将stringstream中的内容通过复制的方式给出,而 .str()是通过 返回一个字符串的方式然后在赋值给出。
2.删除stringstream流中的数据
s.str(""); //将stringstream底层管理的string对象设置为""。
s.clear(); //将上次转换状态清空掉
上面的代码,首先将stringstream中的字符串给变为空字符串,然后用clear函数将状态清空。关于这个状态又是一个知识点:
stringstream底层在一次转换完成后会把标志位记成badbit这时代表的是流不可转换数据,使用clear后变成goodbit流才可转换。但是clear只是更改标记位而不会对内置的字符串进行更改所以这时需要将str置为空。
3.stringstream的常见用法:
1.将数据转换为字符类型
int a = 10;
string sa;
stringstream s;
s << a; //将int类型的a放入输入流
s >> sa; //从s中抽取前面插入的int类型的值,赋值给string类型(方式一)
cout << sa << endl;
2.字符串的拼接
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string rets;
stringstream s;
s << "2021" << "dragon"; //将多个字符串放入stringstream中
s >> rets; //方式一获取
cout << rets << endl;
s.str(""); //将stringstream底层管理的string对象设置为空字符串
s.clear(); //将上次转换状态清空掉
s << "Thanks" << " " << "for" << " " << "reading"; //将多个字符串放入stringstream中
rets = s.str(); //方式二获取
cout << rets << endl;
return 0;
}
4.stringstream能使用的面试题:
1.统计字符串的个数
输入:“hello world c plus plus”
输出:5
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
string str = "hello world c plus plus";
int count = 0;
stringstream ss(str);
string word;
while (ss >> word)
count++;
cout << count << endl;
return 0;
}
很简单因为使用 >> 符号是以空格或者换行为分割符的,所以只要统计总共有几次输入进流中即可。
2.反转字符串中的单词
class Solution {
public:
string reverseWords(string s)
{
string res,temp;
stringstream ss(s);
while(ss>>temp)
{
cout<<temp<<endl;
res = temp + " " + res;
}
if(!res.empty())
{
res.pop_back();
}
return res;
}
};
这题也很简单,将s 传入 ss的构造函数中,这时ss的底层里保存的就是s。此时通过>>流提取符号放入temp中,将数据类似头插的方法将temp放到res前面最后再把最后的空格删除。从这里也可以看出stringstream的>> 流插入符号是从字符串头的位置开始插入的,所以才能让temp中每次只出现一个字符串。