当前位置: 首页 > news >正文

【C/C++】IO流

目录

前言:

一,C语言的I/O流

二,C++的I/O流

2-1,C++标准IO流

2-2,IO流的连续输入


前言:

“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据的抽象描述。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.h>
int main()
{
char password[20] = { 0 };
printf("请输入密码: ");
scanf("%s", password);
printf("请确认密码(Y/N): ");
char input = 0;
scanf("%c", &input);//注意:这块开始出问题
if (input == 'Y')
printf("确认成功\n");
else
printf("确认失败\n");
return 0;
}

scanf 函数的原理就是行缓冲,即输入回车('\n')就会刷新缓冲区。不过需要注意,用户最后输入的回车也会储存在缓冲区。上面代码的问题就出现在scanf的行刷新,当输入完 password 并回车后,缓冲区中还存在回车这个残留字符,当再次进行input流操作时,缓冲区将其字符赋给input并自动刷新(因为缓冲区已经没有数据了),即input == '\n',就会出现上面那种情况。这里需要将缓冲区里面残留字符给去掉或使用 fflush强制刷新缓冲区,但 fflush在有些编译器是没有的,它不属于C标准,不推荐使用。

#include <stdio.h>
int 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");
else
printf("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-1,C++标准IO流

C++的I/O底层原理与C语言一样,但C++系统实现了一个庞大的类库来实现I/O流操作,其中ios为基类,其他类都是直接或间接派生自ios类。

其中,istream(输入流)和ostream(输出流)是I/O(输入/输出)流类的重要组成部分,它们定义了进行I/O操作的基本接口。cin、cout、cerr、clog都是istreamostream的实例。这两个类及其派生类(如ifstreamofstream等)允许程序员以一种简洁且灵活的方式与各种数据源(如文件、控制台等)进行交互。

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-2,IO流的连续输入

编程算法中有些情况会出现不断输入的情况,如: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;
else
return 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 <sstream>
using 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;
}

http://www.jsqmd.com/news/927862/

相关文章:

  • 第17章:安全边界权限与风险控制
  • 【北京朝阳区】房屋修缮指南:防水补漏、瓷砖空鼓与白蚁消杀全解析 - 鲁顺
  • 监控画面总有噪点?深入浅出聊聊海思/安霸芯片里的3D降噪技术到底是怎么工作的
  • 告别sinfo的‘简陋’输出:手把手教你用Bash脚本打造Slurm集群状态监控面板
  • CANN/asc-devkit:DCache访问优化
  • Deliberate AI绘图模型深度解析:从v1到v6的进化之路与核心功能揭秘
  • DeBERTa-v3-large_boolq完整指南:从安装到推理的终极教程
  • Umi-OCR双层PDF转换技术深度解析与实战指南
  • PingFangSC字体包技术指南:跨平台中文字体渲染架构方案深度解析
  • 从0到1部署ruadapt_qwen2.5_3B_ext_u48_instruct_v4:环境配置、依赖安装与测试完整教程
  • 2024年Intel OneAPI更新后,VASP 6.3.2编译安装避坑全记录(附常见错误解决)
  • 如何快速上手Amber模型?从环境配置到文本生成的完整指南
  • [开源] 门急诊药房语音核验助手:面向基层断网场景的处方-药品双码核验系统,本地规则驱动、离线播报、联网可扩展解释
  • 【读书笔记】《架构整洁之道》核心观点提炼
  • swin-small-finetuned-cifar100模型训练揭秘:超参数选择与性能优化技巧
  • AI时代职业重塑:从人机协同到技能升级的实战指南
  • A/B测试加速实战:方差缩减与贝叶斯方法提升实验效率
  • CANN/ops-blas sspmv算子实现
  • 如何在Stable-Worldmodel中实现warm-start规划?提升求解效率的关键技巧
  • GPT-2 Large与其他GPT模型对比:如何选择最适合你项目的语言模型
  • VTK太复杂?试试用C#的ActiViz库:5步搞定三维点云可视化(避坑指南)
  • AI重塑ITSM:从技术顾问到社区构建者的实践与思考
  • 深入systemd:从‘ovsdb-server.service is not running’错误理解Linux服务管理
  • 深度解析OpCore-Simplify:自动化OpenCore EFI配置的技术实现
  • 解决常见问题:Qwen3.6-27B-OBLITERATED使用中的10个疑难解答
  • RoBERTa-large-sst2开发者指南:5个自定义训练与模型优化技巧
  • 如何高效自动化下载国家中小学智慧教育平台电子课本?tchMaterial-parser实用指南深度解析
  • 告别采样负电压!用差分运放给MCU设计一个‘零压线’信号调理电路
  • [开源] 医疗大模型知识盲区检测与可视化系统:面向临床决策者的AI能力边界认知工具
  • 虚拟化浪潮与元宇宙演进:从技术架构到社会影响深度解析