IO流的简单介绍
(PS:这得学好,不然到Linux那边会蒙,还是得重新学)
什么是IO流?
C++ IO流(Input/Output Stream) 是C++标准库中用于处理输入和输出操作的核心机制,通过“流”(数据的有序序列)的抽象概念,实现数据在程序与外部设备(如键盘、屏幕、文件等)之间的传输。
核心本质:
- 流的抽象:将数据传输过程模拟为“水流”,数据从“源”(如文件、键盘)流向程序(输入流),或从程序流向“目的地”(如屏幕、文件)(输出流)。
- 设备无关性:无论操作的是键盘、屏幕还是文件,都使用统一的接口(如 << 、 >> 运算符),无需因设备不同修改核心代码。
在此之前,我们先来了解一些关于C语言的IO(printf,scanf)
scanf(): 从标准输入设备(键盘)读取数据,并将值存放在变量中。
printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)
其中,在它们进行数据的输送的过程中,并不是已经发过去的,而是通过输入/输出缓冲区来进行传输的!
为什么要有缓冲区呢?
平衡高速CPU与低速I/O设备(如磁盘、键盘、显示器)的速度差异,减少I/O操作次数,从而提升程序整体效率。
屏蔽掉低级I/O的实现:低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序。
可以使用这部分的内容实现“行”读取的行为,计算机中没有行的概念的,这个缓存区就充当了行的条件,当读完缓冲区里面的内容,就当作一行。具体的优势:
其具体作用可分为以下3点:(先了解,后面我们讲解Linux的时候会具体讲解的,到时候就会更加理解下面的意思的!)
1. 降低硬件操作开销:I/O设备(尤其是磁盘)的单次读写操作存在固定“启动成本”(如磁头寻道、电机转动)。缓冲器会先暂存数据,积累到一定量后再一次性与设备交互,大幅减少操作次数,降低硬件资源消耗。2. 减少CPU等待时间:若没有缓冲,CPU每次执行I/O操作时都需等待设备响应(“阻塞等待”),导致CPU资源闲置。有缓冲时,CPU只需将数据写入/读取到缓冲器(高速内存操作),即可继续执行其他任务,后续由操作系统或硬件自行完成缓冲器与I/O设备的同步。
3. 优化数据传输效率:多数I/O设备(如网络、磁盘)更适合“批量传输”而非“字节级传输”。缓冲器能将零散的小数据块整合为连续的大数据块,匹配设备的传输特性,减少数据传输中的冗余开销。
流的特性
有序连续、具有方向性
eg:C++中,我们从键盘输入数据,显示器上显示出来,这也是一种流。
C++IO流
C++中,有专门的流的库,即IO流
1. 头文件 <ios>
- 核心类: ios_base 、 ios- 作用:提供IO流的基础类和状态管理功能(如格式标志、错误状态等),是其他IO流类的基类,不直接用于普通IO操作,主要为上层类提供底层支持。
2. 头文件 <istream>
- 核心类: istream- 作用:定义输入流的接口,提供从流中读取数据的功能, cin 是其常用对象(用于标准输入,如键盘输入)。
3. 头文件 <iostream>
- 核心类: iostream (由 istream 和 ostream 派生)- 作用:同时支持输入和输出操作,整合了 <istream> 和 <ostream> 的功能。常用对象有 cin (输入)、 cout (标准输出,如屏幕打印)、 cerr (标准错误输出,无缓冲)、 clog (标准错误输出,有缓冲)。
4. 头文件 <fstream>
- 核心类: ifstream (文件输入流)、 ofstream (文件输出流)、 fstream (文件读写流)、 filebuf- 作用:用于文件的输入输出操作,支持对磁盘文件的读取、写入和修改。例如, ifstream 可打开文件并读取内容, ofstream 可创建或覆盖文件并写入内容。
5. 头文件 <sstream>
- 核心类: istringstream (字符串输入流)、 ostringstream (字符串输出流)、 stringstream (字符串读写流)、 stringbuf- 作用:用于字符串的输入输出操作,可将字符串当作流来处理,实现字符串与其他类型数据的转换(如将字符串中的数字提取为整数)。
ps:
1.在使用时候必须要包含文件并引入std标准命名空间
2.cin:标准输入:即程序代码->硬件设备
3.cout标准输出:即硬件设备->程序代码。
4.cout、cerr、clog是ostream类的三个不同的对象,因此这三个对象现在基本没有区别,只是应用场景不同
5.cin的特性:cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据。(这就是为什么我们在输入数据没按下回车时,它会阻塞住,即在缓冲区那里阻塞住)。
6.输入的数据类型必须与要提取的数据类型一致,否则出错
证明:
ps:大概了解一下:状态:goodbit,eofbit,failbit,badbit
7.空格和回车都可以作为数据之间的分格符
但是,对于字符串的话,我们无法使用cin了,因为字符串我们无法判断空格是字符还是分隔符。所以我们一般不用这个
那么我们会使用哪个呢?
单字符:cin(跳过前导的空格,回车,读取第一个非空白字符),cin.get()(读取输入缓冲区中的第一个字符,包括空格,回车)
含空格字符串:getline(cin,str)(包<string>头文件)(会读取一整行内容,包括空格,一直到遇到回车为止)
8.cin和cout可以直接输入和输出内置类型数据,原因:标准库已经将所有内置类型的输入和
输出全部重载了
9.自定义类型数据需要自己重载。
看看我们写OJ的时候,
对于IO类型的算法,一般都需要循环输入:
输出:严格按照题目的要求进行,多一个少一个空格都不行
它在汇编那里实质上也调了三个operator();
调用的是operator>>,返回值是istream类型的对象,那么这里可以做逻辑条件值,源自于istream的对象又调用了operator bool,operator bool调用时如果接收流失败,或者有结束标志,则返回false。
文件IO
文件内容的数据格式分为二进制文件和文本文件
步骤:
1. 定义一个文件流对象
ifstream ifile(只输入用)
ofstream ofile(只输出用)
fstream iofile(既输入又输出用) 2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系 3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写4. 关闭文件
fwrite:二进制读写
当在文件中建好的时候,打开发现是一串我们看不懂的字符,内存中与文件中的表达式是不一样的,内存中是以12字节存储的,而且存的是补码。
fputs:以字符串读写
用这个接口,打开文件时就可以正常看懂了。
atoi
字符串转整形
to_string
整形转字符串,头文件string
ofstream
从文件写数据
注意:
这里如果用string的话会坑:
1. 字符数组( char[] )的“安全”性
字符数组是把字符串的每个字符直接存在数组里(类似“把内容装在盒子里”)。不管字符串多长,数据本身都“老老实实”存在数组的内存空间中。所以进程 A 把字符数组的内容写出去,进程 B 读的时候,能直接拿到“实实在在的字符串内容”。
2. string (C++ 标准库的字符串类)的“特殊”存储
string 内部不是简单地把所有字符存在自己的“小盒子”里:
- 当字符串比较短时(不同编译器/环境有差异,比如 Linux 和 Windows 表现不同), string 可能会用“内部缓冲区”直接存字符(类似字符数组的行为)。- 但当字符串比较长时, string 会“偷懒”:它不在自己的内存里存所有字符,而是只存一个“指针”,指向另一片内存(堆上)里真正的字符串内容。
3. 进程间二进制读写的“矛盾点”
进程是相互独立的内存空间(一个进程的“地址”,在另一个进程里是“无效地址”)。
如果用 string 做进程间二进制读写:
- 你写出去的,其实是 string 内部的“指针”(以及一些辅助信息),不是真正的字符串内容。- 另一个进程读的时候,拿到的是“上一个进程里的指针”,但这个指针在自己的内存空间里根本无效,就像“拿着别人家里的钥匙开自己家的门”,完全读不出正确内容——这就是所谓“相当于内存指针出问题了”。
4. 结论:二进制读写别用 string 直接传
如果要在进程间传递字符串的二进制数据,不能直接用 std::string 或 string 类对象来读写。因为它存的“指针”在跨进程时毫无意义,应该用**字符数组( char[] )**或者“手动管理的连续内存”来存储字符串内容,再进行进程间传输。
ifstream
从文件读数据
总结:
特性 ifstream ofstream 核心功能 从文件读数据 向文件写数据 默认打开模式 ios::in(读取) ios::out (写入,覆盖原文件) 典型使用场景 读取配置文件、日志文件内容等 生成日志、保存计算结果等 操作符使用 用 >> 读数据 用 << 写数据
istringstream
头文件#include<sstream>
字符串输入流类,用于从字符串中提取数据
区别:
工具 操作对象 核心能力 istringstream 字符串 从字符串中提取多类型数据 cin 标准输入(控制台) 从控制台提取数据 ifstream 文件 从文件中提取数据
ostringstream
头文件#include<sstream>
字符串输出流类,用于向字符串写入(拼接)数据。
stringstream
头文件#include<sstream>
在C语言中,如果想要将一个整形变量的数据转化为字符串格式,如何去做?
1. 使用itoa()函数:
2. 使用sprintf()函数
但是两个函数在转化时,都得需要先给出保存结果的空间,那空间要给多大呢,就不太好界定,而且转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃
特性:stringstream结合了上面istringstream和ostringstream的特性。
所以它也可以进行字符串拼接,拆分。
另外,它还可以进行数据类型转化(字符串转整数)
1.stringstream实际是在其底层维护了一个string类型的对象用来保存结果。
2. 多次数据类型转化时,一定要用clear()来清空,才能正确转化,但clear()不会将stringstream底层的string对象清空。
3. 可以使用s. str("")方法将底层string对象设置为""空字符串。
4. 可以使用s.str()将让stringstream返回其底层的string对象。
5. stringstream使用string类对象代替字符数组,可以避免缓冲区溢出的危险,而且其会对参
数类型进行推演,不需要格式化控制,也不会出现格式化失败的风险,因此使用更方便,更安全。
好了,关于IO的简单了解就到这里,以后用到再回顾就行了。希望大家一起进步!
最后,到了本次鸡汤环节:
贴上最近看到一句话:
努力这件事,绝不是摇旗呐喊、激情澎湃,他就是枯燥乏味的无限循环,当你真正适应了这种状态的时候,你就会发现你与周围的人变得不一样了,加油。