目录前言一C语言的I/O流二C的I/O流2-1C标准IO流2-2IO流的连续输入前言“流”即是流动的意思是物质从一处向另一处流动的过程是对一种有序连续且具有方向性的数据的抽象描述。I/O是指输入输出设备。C/C的I/O流是指信息从外部输入设备如键盘向计算机内部如内存输入和从内存向外部输出设备显示器输出的过程。它的特性是有序连续性和方向性。一C语言的I/O流C语言中我们用到的最频繁的输入输出方式就是scanf()与printf()。scanf从标准输入设备(键 盘)读取数据并将值存放在变量中。printf将指定的文字/字符串输出到标准输出设备(屏幕)注意宽度输出和精度输出控制。除此外C语言还提供了类似于fprintf、fscanf等等专门控制I/O的函数接口。C语言借助了相应的缓冲区来进行输入与输出缓冲器的专门讲解缓冲区如下图所示输入输出缓冲区优点1可以屏蔽掉低级I/O的实现低级I/O的实现依赖操作系统本身内核的实现所以如果能够屏蔽这部分的差异可以很容易写出可移植的程序。2减少因频繁地、小块地读写数据而产生的性能开销。这里的缓冲区可一次性存入这些数据等到刷新缓冲区时将进行输入/输出。C语言对于I/O这块需注意缓冲区的刷新我们来观看以下C语言的代码问题。#include stdio.hint main(){char password[20] { 0 };printf(请输入密码: );scanf(%s, password);printf(请确认密码(Y/N): );char input 0;scanf(%c, input);//注意这块开始出问题if (input Y)printf(确认成功\n);elseprintf(确认失败\n);return 0;}scanf 函数的原理就是行缓冲即输入回车(\n)就会刷新缓冲区。不过需要注意用户最后输入的回车也会储存在缓冲区。上面代码的问题就出现在scanf的行刷新当输入完 password 并回车后缓冲区中还存在回车这个残留字符当再次进行input流操作时缓冲区将其字符赋给input并自动刷新因为缓冲区已经没有数据了即input \n就会出现上面那种情况。这里需要将缓冲区里面残留字符给去掉或使用 fflush强制刷新缓冲区但 fflush在有些编译器是没有的它不属于C标准不推荐使用。#include stdio.hint main(){char x 0, y 0;scanf(%c, x);printf(x %c\n, x);//将缓冲区中的\n拿出即清理缓冲区若需要清理大部分字符这里要使用循环语句getchar();scanf(%c, y);if (y \n)printf(y \\n\n);elseprintf(y %c\n, y);return 0;}输出一没有getchar清理缓冲区字符输出二getchar清理缓冲区字符C中的cin是一种格式化输入拿取数据时自动跳过开头所有空白符如空格、回车(\n)全都忽略。只有读取有效数据且读到下一个空白符才会停止其中空白符会留在输入缓冲区里。因此平常多次使用cin从缓冲区拿取数据时不会出现任何问题但是getline原理与cin不同getline按行读取不会跳过开头任何空白符是直接从当前缓冲区位置开始读取一直读到回车\n为止最后会把\n从缓冲区里取走并丢弃因此才会出现上面情况。cin中的ignore接口可以清空缓冲区并丢弃缓冲区里的字符。cin.ignore(256, \n);第一个参数表示最多忽略256个字符第二个参数表示终止条件当读取到\n时就立刻停止忽略二C的I/O流2-1C标准IO流C的I/O底层原理与C语言一样但C系统实现了一个庞大的类库来实现I/O流操作其中ios为基类其他类都是直接或间接派生自ios类。其中istream输入流和ostream输出流是I/O输入/输出流类的重要组成部分它们定义了进行I/O操作的基本接口。cin、cout、cerr、clog都是istream和ostream的实例。这两个类及其派生类如ifstream、ofstream等允许程序员以一种简洁且灵活的方式与各种数据源如文件、控制台等进行交互。istream输入流定义了从数据源如文件、控制台等读取数据的基本操作它包含了一系列以operator形式出现的成员函数用于读取不同类型的数据。其中我们常用于从控制台读取数据的cin就是istream的一个实例而cin只有把输入缓冲区中的数据取完后即刷新缓冲区才要求输入新的数据补足了C语言的scanf的缺陷。ostream输出流定义了向数据目标如文件、控制台等写入数据的基本操作它包含了一系列以operator形式出现的成员函数用于写入不同类型的数据。其中我们常用于向控制台写入数据的cout就是ostream的一个实例而平常与cout连用的std::endl操作符不仅插入了一个换行符还刷新了与std::cout关联的输出缓冲区。C标准IO流除了cout标准输出和cin标准输入外还有cerr用来进行标准错误的输出以及clog进行日志的输出。从上图可以看出cout、 cerr、clog是ostream类的三个不同的对象因此这三个对象现在基本没有区别只是应用场景不同一般情况下cout用的比较多。C的标准IO流之所以能够直接输出内置类型数据是因为标准库已经将所有内置类型的输入和输出全部重载了。对于自定义类型若需要支持cin和cout的标准输入输出需要对 和 进行重载。总的来说C的IO流是使用面向对象运算符重载的方式实现的它识别类型的本质是函数重载内置类型可以直接使用是因为库里面istream/ostream类型已经实现了自定义类型则需要自己重载 和 也就是说C的这种IO模式能够更好的兼容自定义类型流插入和流提取。2-2IO流的连续输入编程算法中有些情况会出现不断输入的情况如while (cin a){.....}结束输入的情况很多时候都是使用快捷键Ctrl c解决Ctrl c是向系统内部发送结束当前进程的信号直接暴力杀死进程。实际上我们看到使用while (cin a){.....}去流中提取对象数据时调用的是operator返回值是 istream类型的对象即while(cin a)实际上是while (operator(cin, a))那么这里可以做逻辑条件值源自于istream的对象又调用了operator bool()operator bool()调用时如果接收流失败或有结束标志时则返回false。任何类型只要想判断只用重载一个operator bool()即可。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){}//重载bool类型用于循环语句的判断operator bool(){//这里假设输入_year为0时结束if (_year 0)return false;elsereturn true;}private:int _year;int _month;int _day;};//实现Date类的流插入和流提取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(2024, 6, 7);cout 输出: d endl;while (d){cin d;cout 输出: d endl;}return 0;}三字符串流在C中字符串流是标准库提供的一种I/O流类用于在字符串和流之间进行数据交换。它们允许你像操作文件流一样操作字符串属于iostream类库的一部分比如读写操作。这里重点说明stringstream。stringstream是双向字符串流是C 标准库中的一个类在sstream头文件中。它结合了输入流istream和输出流ostream的功能允许我们使用输入操作符和输出操作符从stringstream对象中读取数据和将数据写入stringstream对象中即使用像操作文件一样操作字符串。最后要说明的是stringstream常用于将一种数据类型转换为另一种数据类型。当我们使用 读取一个数据项时它会在遇到第一个空白字符时停止读取并将读取到的内容转换为相应的数据类型。stringstream的使用如下#include iostream#include sstreamusing namespace std;int main(){stringstream ss(1 2 3);string str1 ss.str();// str用于获取ss整个内容string str2;ss str2;cout str1: str1 endl;cout str2: str2 endl;cout endl;ss Name: 张三 , Age: 18;str1 ss.str();cout str1: str1 endl;// cout ss endl; 注意这种操作是错误的因为stringstream本身就是字符串流return 0;}