c++ 专题 1:IO stream

本文详细介绍了C++中的IO Stream,包括流的状态、操控器、格式化、缓冲区及其性能。讨论了流的基本结构、标志位、文件标志,以及如何使用fstream和sstream进行文件和字符串操作。此外,还探讨了与C语言IO的性能对比和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



前言

所谓 stream 就是一条数据流,字符序列在其中“川流不息”。

c++ 中的 stream 是由某个 class 定义出来具有特定性质的对象。其中输出动作被抽象为数据流进 stream,输入动作被抽象为数据流出 stream。

c++ IO stream 的基本结构如下:
在这里插入图片描述

注意:

  • 不能拷贝或对 stream 对象赋值,因此 stream 作为形参/返回类型时要用引用的形式;
  • stream 不能声明 const(读写一个 IO 对象会改变其状态);

一些概念:

  • stream 定义了 4 个类型为 istream 和 ostream 的全局标准 stream 对象,cin,cout,cerr,clog,分别对应于标准 I/O 通道 ;

  • 流操作符:basic_ostream/basic_istream 将 >>/<< 定义为输入/输出操作符,

    • >>:按箭头方向将 stream 存储于第二实参;
    • <<:把第二实参按箭头方向发送到相应的 stream 去;
    • 两者都返回第一个实参,即输入输出 stream ;

    说明:stream 对所有基础类型均重载了输入/输出操作符,不包括 void 和 nullptr_t。可以通过重载类类型的 operator<<,扩展 stream 操作的类型;这也使得 c++ 的 IO 操作相对于 c 的 printf I/O 机制,程序不再需要指定输出的数据类型,只要针对不同类型进行重载,就可以保证编译器自动推断出正确的打印函数。

    示例:考虑如下表达式:

    cout<<x<<"times"<<y<<"is "<<x*y<< endl;
    

    operator<< 从左向右执行,因此先执行 cout<< x;此表达式返回第一个实参 cout 的引用,因此接下来执行 cout<<“times”。

  • 标志位:标志位用来表示流的相关信息,标志位被实现为一种位掩码,可以同 | 来组合标志位,以及 & 来检测标志为。其中包括:

    • 状态标志:ios_base 包含 4 个 iostate 类型的静态常量数据成员用于描述流的状态 badbit,failbit,eofbit 和 goodbit。当事件发生,相应的位被置位;相反,状态位都被清除时,说明 stream 一切正常。同时,ios_base 提供了一组成员函数用于检查,清除或设置相应的状态位;
    • 格式标志:ios_base 定义了 fmtflags 类型用来存储数据输入/输出时的格式;
    • 文件标志:ios_base 定义了 openmode 类型用于控制文件打开模式;
  • 操控器:操控器是专门用来操控 stream 的一种对象,用于刷新缓存或格式化输入/输出数据;如下一组基本操控器定义于 或 用于刷新缓存;其余的操控器用于格式化数据的输入/输出,定义于 ;

    操控器 说明
    endl 输出一个换行符,并清空缓存
    ends 输出一个终止符,并清空缓存
    flush 清空缓存
    ws 读入时忽略空格
  • 格式化:设置 stream 格式的方式有 2 种;

    成员函数 说明 操控器 说明
    setf(flags) 添加 flags,返回之前的 flags setiosflags(flags) 调用 etf(flags)
    setf(flags, grp) 添加 flags,并清除本组其余 flags,返回之前的 flags resetiosflags(grp) 调用 setf(0,grp)
    unsetf(flags) 只清除 flags
    flags() 返回 flags
    flags(flags) 将当前 flags 设置为参数指定的 flags,返回所有之前的 flags
    copyfmt(stream) 从 stream 中复制所有的格式定义

    额外的,对于某些格式标志还提供了专门的控制器;

    示例:

    cout.setf(ios::showpos | ios::uppercase); //添加 showpos,uppercase
    cout.setf(ios::hex, ios::basefield);      //保证仅设置 basefield 组中的 hex 位
    cout.unsetf(ios::uppercase);              //清除 uppercase
    
    cout << resetiosflags(ios::adjustfield)     //清除 adjustm 
    << setiosflags(ios::left);             //添加 left 
    

额外的:C++ 标准库并未提供 “运用文件描述符将一个 stream 关联到某个 I/O 通道” 的方式。

流的状态

  • goodbit 被定义为 0,设置 goodbit 并不是将哪个位置位,而是意味着将其他位被清除了;
  • 由于其静态属性,这些 flag 都只能反映最后一次操作的 stream 状态(甚至无法确定究竟是这一次或先前操作导致这个结果);
  • 同样由于其由于其静态属性,可以通过实例化对象访问 cin.goodbit 或直接访问 ios_base::badbit;
状态位 含义 函数 作用
ios_base::badbit 指出流已崩溃,流无法在使用 s.bad() badbit 置位,返回 true
ios_base::failbit 指出 io 操作失败,置位后,流还可以继续使用; s.fail() failbit 或 badbit 置位,返回 true
ios_base::eofbit 文件末尾,同时设置 failbit s.eof() eofbit 置位,返回 true
ios_base::goodbit 指出流未处于错误状态,此值保持为 0; s.good() 所有错误位均未被置位的情况下返回 true
s.clear(iostate) 清除所有状态位,设置状态位 flags
s.clear() 清除所有状态位
s.setstate(iostate) 设置状态位 flags
s.rdstate() 返回流的当前状态

示例:检查 failbit 是否设置,若设置则清除:

if(strm.rdstate() & ios::failbit){
   
cout <<"failbit was set"<< endl;
	strm.clear(strm.rdstate() & ~ios::failbit);  //仅清除 failbit 
}

进一步的,ios_base 重载了 operator bool() 用于在控制结构中反映流的状态:

成员函数 意义
operator bool() Stream 是否未出错(相当于 !fail() )
operator !() Stream 是否已出错(相当于 fail() )

示例:考虑表达式

if(std::cin>>x){
    }  

其中表达式 std::cin>>x 会返回 cin,所以读入 x 后上述语句变为:

if(std::cin){
    }  

此时的 cin 被用于条件判断,接着 cin 调用 operator bool,根据 stream 的状态返回 stream 是否发生错误。

操控器的实现

IO 操作符重载了以函数指针为参数的版本,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值