🥑IO类
2022-5-18
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

 

C语言的输入输出

C语言中用到的最频繁的输入输出方式就是scanf()printf()
  • scanf()从标准输入设备(键盘)读取数据,并将值存放在变量中
  • printf()将指定的文字/字符串输出到标准输出设备(屏幕),使用时注意宽度输出和精度输出控制。
输入输出缓冲区:
  1. 可以屏蔽掉低级I/O的实现,低级I/O的实现依赖操作系统本身内核的实现,所以如果能够屏蔽这部分的差异,可以很容易写出可移植的程序。
  1. 可以使用这部分的内容实现“行”读取的行为,对于计算机而言是没有“行”这个概念,有了这部分,就可以定义“行”的概念,然后解析缓冲区的内容,返回一个“行”。
 
scanfprintf是和控制台、终端相关的,还有与文件相关的:
  • fscanf针对所有输入流的格式化的输入语句 —— stdin/文件
  • fprintf针对所有输出流的格式化输出语句 —— stdout/文件
还有用来序列化字符串的:
  • sscanf从一个字符串中读取一个格式化的数据
  • sprintf把一个格式化的数据,转换成字符串
 

C++IO流

C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。
特性是:有序连续、具有方向性。为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能。
C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios
notion image
 
头文件iostream定义了用于读写流的基本类型,fstream定义了读写命名文件的类型,sstream定义了读写内存中 string 对象的类型:
notion image
宽字符版本的IO类型和函数的名字以w开始,如wcinwcoutwcerr分别对应cincoutcerr。它们与其对应的普通char版本都定义在同一个头文件中,如头文件fstream定义了ifstreamwifstream类型。
 
IO 类型之间的关系
不论是哪一种方式的 IO,从控制台或者文件或者字符串中进行输入输出,或者是字符宽度的不一样都不会影响操作的使用形式,都是使用<<>>读取数据。之所以能够这样的做的原因在于不同种类的stream使用了继承。ifstreamistringstream 都继承自 istream,所以可以将 ifstreamistringstream 类型对象当做 istream 对象来使用,这样可以将>>用于从 ifstreamistringstream 中读取数据。
 
部分IO库设施:
  • istream:输入流类型,提供输入操作
  • ostream:输出流类型,提供输出操作
  • cinistream 对象,从标准输入读取数据
  • coutostream 对象,向标准输出写入数据
  • cerrostream 对象,向标准错误写入数据
  • >> 运算符:从 istream 对象读取输入数据
  • << 运算符:向 ostream 对象写入输出数据
  • getline 函数:从 istream 对象读取一行数据,写入 string 对象
 

C++标准IO流

C++标准库提供了4个全局流对象cincoutcerrclog
  1. 使用cout进行标准输出,即数据从内存流向控制台(显示器)
  1. 使用cin进行标准输入即数据通过键盘输入到程序中
  1. C++标准库还提供了cerr用来进行标准错误的输出
  1. 以及clog进行日志的输出
coutcerrclogostream类的三个不同的对象,基本没有区别,只是应用场景不同。
 
 

IO对象无拷贝或赋值

不能拷贝或对IO对象赋值:
由于IO对象不能拷贝,因此不能将函数形参或返回类型定义为流类型。进行IO操作的函数通常以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用不能是 const 的。
 

条件状态

IO操作一个与生俱来的问题是可能发生错误。一些错误是可恢复的,而其他错误则发生在系统深处,已经超出了应用程序可以修正的范围。
下表列出了IO类所定义的一些函数和标志,可以帮助我们访问和操纵流的条件状态
notion image
一个IO错误的例子:
如果输入Boo将会读取失败,输入操作符想要获取一个 int类型的值,但是遇到了字符B,这时cin对象中将会产生一个错误状态。同样,如果输入end-of-file时,cin也会处于错误状态。
 
一个流一旦发生了错误,此流对象接下来的IO操作将会全部失败,只有当流对象没有错误时才能对其进行读取和写入。由于流可能处于错误的状态,通常需要在使用前检查流是否处于正确状态。检查流对象的状态的最简单的方式就是将其放到一个条件中去:
while条件检查 >>表达式返回的流的状态,如果操作成功,这个状态将保持有效,并且条件检查返回true
 

检查流的状态

将流放到条件中只是告知流是否有效,并没有告知发生了什么。有时需要知道更详细的信息。IO 对象使用一个机器相关的iostate类型来承载状态信息。这个类型是一个bit的集合。IO 类同时定义了 4 个常量值来表示不同的bit模式,它们用于表示不同的IO状态。这些值可以通过位操作符来测试和设置iostate值。
notion image
  • badbit表示发生系统级的错误,如不可恢复的读写错误。通常情况下一旦badbit被置位(即代表badbit的那一位设置为1),流就无法再使用了
  • failbit表示发生可恢复的错误,如期望读取一个数值,却读出一个字符等错误。这种问题通常是可以修改的,流还可以继续使用
  • 当到达文件的结束位置时,eofbitfailbit都会被置位
  • goodbit 的值为 0 (即没有一位发生置位),表示流未发生错误
  • 如果 badbitfailbiteofbit 任何一个被置位,检测流状态的条件都会失败
 
标准库还定义了一组成员函数来查询这些标志位的状态。 good() 成员函数在所有错误位均未置位的情况下返回true,而 bad()fail()eof() 则在对应错误位被置位时返回true。此外,在badbit被置位时,fail()也会返回 true。
查看流的整体状态使用 goodfail 函数,将流对象用于条件中时,相当于调用 !stream.fail() ,eofbad 操作则仅仅只揭示特定的错误是否发生。
 

管理条件状态

流对象的 rdstate 成员返回一个 iostate 值,表示流的当前状态。setstate 成员用于将指定条件置位,表示发生了对应错误。clear 成员是一个重载成员:无参版本清除(复位)所有错误标志位,执行clear() 后,调用good会返回true
 
clear还有一个有一个实参的版本,这个实参的类型是 iostate 值用于表示新的流状态,它是将参数中的值全部替换掉流中的状态值。为了关闭一个单独的状态,使用 rdstate成员函数读取状态值,并用位操作符生成想要的新状态。如以下代码将 failbitbadbit 复位,但是不改变 eofbit 的值:
 

管理输出缓冲

每个输出流都管理一个缓冲区,用于保存程序读写的数据。 如果执行下面的代码:
文本串可能立即打印出来,但也有可能被操作系统保存在缓冲区中,随后再打印。有了缓冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作。由于设备的写操作可能很耗时,允许操作系统将多个输出操作组合为单一的设备写操作可以带来很大的性能提升。
 
 
导致缓冲刷新(即数据真正写入输出设备或文件)的原因有很多:
  • 程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行
  • 缓冲区已满时,需要刷新缓冲。而后新的数据才能继续写入缓冲区
  • 使用操纵符(如 endl)显式刷新缓冲区
  • 在每个输出操作之后,可以用 unitbuf 操纵符设置流的内部状态,从而清空缓冲区。默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的
  • 一个输出流可以被关联到另一个流。这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。默认情况下,cincerr 都关联到 cout,因此,读 cin 或写 cerr 都会刷新 cout 的缓冲区。
 

刷新输出缓冲

  • endl在缓冲中增加一个换行符并刷新
  • flush 操纵符刷新缓冲区,但不输出任何额外字符
  • ends 向缓冲区插入一个空字符,然后刷新缓冲区

unitbuf 操纵子

如果想在每次输出操作后都刷新缓冲区,可以使用 unitbuf 操纵符。它令流在接下来的每次写操作后都进行一次 flush 操作。而 nounitbuf 操纵符则使流恢复使用正常的缓冲区刷新机制。
 
如果程序异常终止,输出缓冲区是不会被刷新的。当一个程序崩溃后, 它所输出的数据很可能停留在输出缓冲区中等待打印。 当调试一个已经崩溃的程序时,需要确认那些你认为已经输出的数据确实已经刷新了。否则, 可能将大量时间浪费在追踪代码为什么没有执行上,而实际上代码已经执行了,只是程序崩溃后缓冲区没有被刷新,输出数据被挂起没有打印而已。
 
 

关联输入和输出流

当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。标准库将coutcin关联在一起,因此下面的语句会导致cout的缓冲区被刷新:
交互式系统通常应该关联输入流和输出流。这意味着包括用户提示信息在内的所有输出,都会在读操作之前被打印出来。
 
使用 tie 函数可以关联两个流。它有两个重载版本:无参版本返回指向输出流的指针。如果本对象已关联到一个输出流,则返回的就是指向这个流的指针,否则返回空指针。tie 的第二个版本接受一个指向 ostream 的指针,将本对象关联到此 ostream
既可以将一个istream对象关联到另一个ostream,也可以将一个ostream关联到另一个ostream
上面代码,为了将一个给定的流关联到一个新的输出流,将新流的指针传递给了tie。为了彻底解开流的关联,传递了一个空指针。每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream
 
  • C++
  • 类成员指针文件输入输出
    目录