C++语法系列之IO流
前言
IO流是C++处理输入输出的一套机制,其中“流”是对一种有序连续且具有方向性的数据的抽象描述,从外部到内部输入和从内部到外部输出这种过程被形象的称为流,C++IO库就是为了实现一些特定的功能。
其实C语言已经有比较完美的一套标准输入输出库了,< stdio.h>(standard IO.h)但是最大的问题是无法支持自定义类型,而C++就解决了这一点
一、C语言中的几种输入和输出的函数
本来是想直接介绍C++的IO库中的函数,想一想先复习一下C语言的,不仅方便和后面的对比,并且本来就有联系,但这里不介绍太详细,稍微回忆一下
1.scanf 和 printf
scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中。printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。
scanf()和printf的功能非常强大–通过格式化字符串来实现
必选部分为: % + 转换说明符(d,f,x,s)…
还可以加上修饰符号,宽度啊,精度啊等等
特别是printf,控制格式方面甚至比C++还好用。
int main()
{int a, b;scanf("% d, % d", &a, &b);printf("%5d%5d\n", a, b);return 0;
}
scanf 和 printf都有返回值,返回值是参数个数。
C语言中,输入输出操作通常经过缓冲区来实现,scanf从缓冲区读取数据,如果缓冲区没有数据则会等待输入,printf将数据读入标准输出缓冲区中,程序结束 / 缓冲区满 / fflush / 的调用 都会导致输出到屏幕上,让缓冲区刷新。
2.sscanf 和 sprintf
s–string 是处理字符串的函数。
sprintf:将格式化数据写入字符串(类似于printf,但输出到内存而非屏幕)。
sscanf:从字符串读取格式化数据(类似于scanf,但输入来自内存而非键盘)。
对于日期类来说还是很好用的。
int main() {char str[100] = "2025-6-4";int year = 1, month = 1, day = 1;int ret = sscanf(str,"%d-%d-%d", &year, &month, &day);cout << "今天是" << year << "年" << month << "月" << day << "日" << endl; //其实这里printf会更好写return 0;
}
int main() {int year = 2025, month = 6, day = 4;char s[100] = {};sprintf(s, "%d-%d-%d", year, month, day);printf("%s\n", s);return 0;
}
输出就不放了很简单
3.fscanf 和 fprintf
f — file – 文件处理
fprintf:将格式化数据写入文件流。
fscanf:从文件流中读取数据。
注意:“w”是写,"r"是读,
#include <stdio.h>
//cstdio.txt
int main() {char s[] = "恭喜你写入文件";FILE* fp = fopen("xxxx.txt", "w");//fputs(s, fp);fprintf(fp, "%s", s);//fputs的效果类似fclose(fp);return 0;
}
int main() {char s[100] = "";FILE* fp = fopen("xxxx.txt", "r");//fgets(s,100,fp);fscanf(fp, "%s", s);//fgets也可以fclose(fp);cout << s << endl;return 0;
}
还可以玩自己所写的代码。
输出我们所在文件夹的代码
int main() {char s[100] = "";FILE* fp = fopen("IO.cpp", "r");//fputs(s, fp);char ch;while (fscanf(fp, "%c", &ch) != EOF){cout << ch;}fclose(fp);return 0;
}
将我们所写代码的文件拷贝到另一个文件中
需要一个文件来读我们的文件,然后还需要一个写进入另一个文件
#include <stdio.h>
//cstdio.txt
int main() {char s[100] = "";FILE* fpw = fopen("other.cpp", "w");FILE* fpr = fopen("IO.cpp", "r");//fputs(s, fp);char ch;while (fscanf(fpr, "%c", &ch) != EOF){fprintf(fpw, "%c", ch);}fclose(fpr);fclose(fpw);return 0;
}
二、C++IO库
C++搞出了一个巨大的库,继承来继承去的,甚至都有菱形继承,但是具体
可以看出点端倪,istream->i + stream输入流,ostream->输出流 cin 继承istream,
cout ,cerr,clog继承ostream,< fstream> --文件,< sstream>–字符串
三、标准IO流
头文件 < iostream>
比C语言的优势:
1.方便输入输出:通过运算符重载实现类型自动识别(如cout << “hello” << 123 << endl),如果用printf还需要写格式化的字符串。
2.扩展性强:支持自定义数据类型的输入输出(重载<<和>>),又重载了所有内置类型。
核心的流对象: -----------------------C语言对应的
1.cin 标准输入流(键盘输入) --scanf()
2.cout 标准输出流(屏幕输出) --printf()
3.cerr 标准错误流(无缓冲输出) --stderr
4.clog 标准错误流((缓冲输出) 无缓冲的stderr
其中clog也进行日志流的输出
注意:1.提取什么类型输入什么类型,因为cin是从缓冲区提取,不对应输入会出错
2.cin无法提取带空格的字符串,这时要使用getline
int main() {string str;getline(cin, str);cout << str << endl;return 0;
}
重载流插入和流提取
ostream& operator << (ostream& out, const T& d)
istream& operator >> (istream& in, T& d)
这个是怎么实现的?
int b;while(cin >> b){}
cin调用operator>> (cin 继承了istream)
istream又去调用operator bool 如果接收流失败,或者有结束标志,则返回false
另外也不建议while(cin >> a )这么写,如果做算法题可以加条件来判断,while(cin >> a&& a != 0) 一般算法题这种输入都是有条件来判断什么时候停止的,
请看下面这段代码:问:这段代码可不可以编译通过?
class INT {
public:friend istream& operator >> (istream& in, INT& a);INT(int a = 1):_a(a){}int get(){return _a;}
private:int _a;
};
istream& operator >>(istream& in, INT& a)
{in >> a._a;return in;
}
int main()
{INT a;while (cin >> a){cout << a.get()<< endl;}return 0;
}
答案是没问题的啊,你可能会问:我不需要重载operator bool?,注意cin >> a返回值是istream& ,istream中本身就有operator bool判断是不是eof,bad等等,你重载的operator bool是判断这种情况的:
if(!a){break;}
四、文件IO流
其实没啥可讲的,和C语言的区别不就是可以支持自定义类型还有 << >>
举例子的代码就不放了,很简单,看一看就行
另外:库里面有一些函数get(),getline(),二进制读写啊,等等建议了解了解
istream
ofstream
另外是不还有个fstream这个看继承关系就是ifstream 和 ofstream的合体,兼容了这两个的性质,可以同时使用。
ifstream ifs(输入)
ofstream ofs(输出)
fstream fs(既输入又输出)
自定义的类就去重载就可以了,没啥好说的
五、字符串IO流
其实也没差多少。。istringstream + ostringstream = stringstream,其中stringstream还是有点用的。istringstream 和 ostringstream的演示:
int main()
{string str = "hello 1 world";istringstream iss(str);string word1,word2;int num;iss >> word1 >> num >> word2;cout << word1 << " " << num << " " << word2 << endl;return 0;
}
输出结果就不放了。
int main()
{string str;ostringstream oss(str);string word1 = "hello", word2 = "world";int num = 1;oss << word1 << " " << word2 << " " << num;cout << oss.str() << endl;return 0;
}
建议加上空格,更好的读入和读出数据。
stringstream 可以通过<<存入字符串,>>拿出字符串
–我第一次应用在一个题上
就是输入一行不知道有多少个要求,我就用上stringstream了,
while (n--){string line;vector<string> v;getline(cin, line);stringstream iss(line);//这里用istringstream 也可以string token;int num;iss >> num;while (iss >> token){v.push_back(token);}}
有数字就可以先读入数字,这个操作同样可以使用getline(cin,str);然后去利用空格分隔str。
注意:1. stringstream实际是在其底层维护了一个string类型的对象用来保存结果。
2. 多次数据类型转化时,一定要用clear()来清空,才能正确转化,但clear()不会将
stringstream底层的string对象清空。
3. 可以使用s. str(“”)方法将底层string对象设置为""空字符串。
4. 可以使用s.str()将让stringstream返回其底层的string对象。
5. stringstream使用string类对象代替字符数组,可以避免缓冲区溢出的危险,而且其会对参数类型进行推演,不需要格式化控制,也不会出现格式化失败的风险,因此使用更方便,更安全。
总结就是:要用的时候就用stringstream,也别整什么istringstream或者ostringstream
总结
本次文章应该是语法系列的最后一期文章,后面会进入STL的更新,虽然STL是C++的一部分,但是我觉得他的内容之多和广足以开一个新的栏目叫STL篇,本篇也应该是近几周的最后一篇,因为要开始期末周的复习了。(感觉C++的东西都没有C语言写的多.。。。。)