IO 类
| 头文件 类型 |
| iostream istream, wistream从流读取数据 |
| ostream, wostream从流写入数据 |
| iostream, wiostream读写流 |
| fstream ifstream, wifstream从文件读取数据 |
| ofstream, wofstream向文件写入数据 |
| fstream, wfstream读写文件 |
| sstream istringstream, wistringstream从 string 读取数据 |
| ostringstream, wostringstream 向 string 写入数据 |
| stringstream, wstringstream 读写 string |
- 为了支持使用宽字符的语言,标准库定义了一组类型和对象来操纵 wchar_t 类型的数据。
wcin wcout wcerr
是分别对应 cin cout cerr
的宽字符版对象
- 宽字符版本的类型和对象与其对应的普通 char 版本的类型定义在同一个头文件中
类型间的转换
- 标准库通过继承机制使我们能忽略这些不同类型的流之间的差异
- ifstream 和 istringstream 都继承自 istream
- ofstream 和 ostringstream 都继承自 ostream
IO 对象无拷贝或赋值
| ofstream out1, out2; |
| out1 = out2; |
| ofstream print(ofstream); |
| out2 = print(out2); |
- 进行 IO 操作的函数通常以引用方式传递和返回流。
- 读写一个 IO 对象会改变其状态,因此传递和返回的引用不能是 const 的
条件状态
帮助我们 访问 和 操作 流的条件状态的函数和标志
| strm::iostate strm 是一种 IO 类型。iostate 是一种机器相关的类型,提供了表达条件状态的完整功能 |
| strm::badbit strm::badbit 用来指出流已崩溃 |
| strm::failbit strm::failbit 用来指出一个 IO 操作失败了 |
| strm::eofbit strm::eofbit 用来指出流到达了文件结束 |
| strm::goodbit strm::goodbit 用来指出流未处于错误状态。此值保证为零 |
| s.eof() 若流 s 的 eofbit 置位,则返回 true |
| s.fail() 若流 s 的 failbit 或 badbit 置位,则返回 true |
| s.bad() 若流 s 的 badbit 置位,则返回 true |
| s.good() 若流 s 处于有效状态,则返回 true |
| s.clear() 将流 s 中所有条件状态位复位,将流的状态设置为有效。返回 void |
| s.clear(flags) 根据给定的 flags 标志位,将流 s 中对应条件状态位复位。flags 的类型为 strm::iostate。返回 void |
| s.setstate(flags) 根据给定的 flags 标志位,将流 s 中对应条件状态位置位。flags 的类型为 strm::iostate。返回 void |
| s.rdstate() 返回流 s 的当前条件状态,返回值类型为 strm::iostate |
查询流的状态
- badbit 表示系统级错误,如不可恢复的读写错误
- 在发生可恢复错误后,failbit 被置位
- 如果到达文件结束位置,eofbit 和 failbit 都会被置位
- goodbit 的值为 0 ,表示流未发生错误。如果 badbit、failbit 和 eofbit 任一个被置位,则检测流状态的条件会失败
- good 在所有错误位均未置位的情况下返回 true
- bad、fail 和 eof 则在对应错误位被置位时返回 true
- 此外 badbit 被置位时,fail 也会返回 true。这意味着,使用 good 或 fail 是确定流的总体状态的正确方法
- 将流当作条件使用的代码就等价于 !fail(),而 eof 和 bad 操作只能表示特定的错误
管理条件状态
- rdstate 返回一个 iostate 值,对应流的当前状态
- setstate 操作将给定条件位置位,表示发生了对应错误
- clear 不接受参数的版本清除(复位)所有错误标志位。执行 clear() 后,调用 good 会返回 true
| auto old_state = cin.rdstate(); |
| cin.clear(); |
| process_input(cin); |
| cin.setstate(old_state); |
- 带参数的 clear 版本接受一个 iostate 值,表示流的新状态
| |
| cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit); |
管理输出缓冲
- 每个输出流都管理一个缓冲区,用来保存程序读写的数据
- 有了缓冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作
导致缓冲刷新的原因有很多
- 程序正常结束,作为 main 函数的 return 操作的一部分,缓冲刷新被执行
- 缓冲区满时,需要刷新缓冲,而后新的数据才能继续写入缓冲区
- 我们可以使用操作符如 endl 来显式刷新缓冲区
- 在每个输出操作之后,我们可以用操作符 unitbuf 设置流的内部状态,来清空缓冲区。默认情况下,对 cerr 是设置 unitbuf 的,因此写到 cerr 的内容都是立即刷新的
- 一个输出流可能被关联到另一个流。在这种情况下,当读写被关联到流时,关联到的流的缓冲区会被刷新。例如,默认情况下,cin 和 cerr 都关联到 cout。此时,读 cin 或写 cerr 都会导致 cout 的缓冲区被刷新。
刷新输出缓冲区
| cout << "hi!" << endl; |
| cout << "hi!" << flush; |
| cout << "hi!" << ends; |
unitbuf
- 如果想每次输出操作后都刷新缓冲区,我们可以使用 unitbuf 操作符
- unitbuf:接下来每次写操作之后都进行一次 flush 操作
- nounitbuf:重置流,使其恢复正常的系统管理的缓冲区刷新机制
| cout << unitbuf; |
| |
| cout << nounitbuf; |
警告:如果程序崩溃,输出缓冲区不会被刷新
关联输入和输出流
tie 有两个重载的版本
- 不带参数:返回指向输出流的指针。如果本对象当前关联到一个输出流,则返回的就是指向这个流的指针,如果对象未关联到流,则返回空指针。
- 带参数:接受一个指向 ostream 的指针,将自己关联到此 ostream。即 x.tie(&o) 将流 x 关联到输出流 o
**我们既可以将一个 istream 对象关联到另一个 ostream,也可以将一个 ostream 关联到另一个 ostream
| cin.tie(&cout); |
| |
| std::ostream *old_tie = cin.tie(nullptr); |
| |
| cin.tie(&cerr); |
| cin.tie(old_tie); |
- 每个流同时最多关联到一个流,但多个流可以同时关联到同一个 ostream
文件输入输出
头文件fstream
定义了三个类型来支持文件 IO
ifstream
从一个给定文件读取数据
ofstream
向一个给定文件写入数据
fstream
可以读写给定文件
- 可以用 IO 运算符(<< 和 >>)来读写文件,可以用
getline
从一个 ifstream
读取数据
| fstream fstrm; 创建一个未绑定的文件流。fstream 是头文件 fstream 中定义的一个类型 |
| fstream fstrm(s); 创建一个 fstream,并打开名为 s 的文件。s 可以是 string 类型, |
| 或者是一个指向 C 风格字符串的指针。 |
| 这些构造函数都是 explicit 的。默认的文件模式 mode 依赖于 fstream 的类型 |
| fstream fstrm(s, mode); 与前一个构造函数类似,但按指定 mode 打开文件 |
| fstrm.open(s) 打开名为 s 的文件,并将文件与 fstrm 绑定。 |
| s 可以是一个 string 或一个指向 C 风格字符串的指针。 |
| 默认的文件 mode 依赖于 fstream 的类型。返回 void |
| fstrm.close() 关闭与 fstrm 绑定的文件。返回 void |
| fstrm.is_open() 返回一个 bool 值,指出与 fstrm 关联的文件是否成功打开且尚未关闭 |
使用文件流对象
| std::ifstream in(ifile); |
| std::ofstream out; |
用 fstream 代替 iostream&
| |
| ifstream input(argv[1]); |
| ofstream output(argv[2]); |
| |
| Sales_data total; |
| if (read(input, total)) { |
| Sales_data trans; |
| while (read(input, trans)) { |
| if (total.isbn() == trans.isbn()) { |
| total.combine(trans); |
| } else { |
| print(output, total) << endl; |
| total = trans; |
| } |
| } |
| print(output, total) << endl; |
| } else { |
| cerr << "No Data?" << endl; |
| } |
成员函数 open 和 close
| ifstream in(ifile); |
| ofstream out; |
| out.open(ifile + ".copy"); |
| |
| if (out) |
- 一旦一个文件流已经打开,它就保持与对应文件的关联。为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件
| in.close(); |
| in.open(ifile + "2"); |
自动构造和析构
| for (auto p = argv + 1; p != argv + argc; ++ p) { |
| ifstream input(*p); |
| if (input) { |
| process(input); |
| } else { |
| cerr << "couldn't open:" + string(*p); |
| } |
| } |
因为 input 是 while 循环的局部变量,它在每个循环步中都要创建和销毁一次。当一个 fstream 对象离开其作用域时,与之关联的文件会自动关闭。在下一步循环中,input 会再次被创建。
文件模式
| in 以读方式打开 |
| out 以写方式打开 |
| app 每次写操作前均定位到文件末尾 |
| ate 打开文件后立即定位到文件末尾 |
| trunc 截断文件 |
| binary 以二进制方式进行 IO |
指定文件模式有如下限制
- 只可以对 ofstream 或 fstream 对象设定 out 模式
- 只可以对 ifstream 或 fstream 对象设定 in 模式
- 只有当 out 也被设定时才可以设定 trunc 模式
- 只要 trunc 没被设定,就可以设定 app 模式,在 app 模式下,即使没有显式指定 out 模式,文件也总是以输出方式被打开
- 默认情况下,即使我们没有指定 trunc,以 out 模式打开的文件也会被截断。为了保留以 out 模式打开的文件的内容,我们必须同时指定 app 模式,这样只会将数据追加到文件末尾;或者同时指定 in 模式,即打开文件同时进行读写操作
- ate 和 binary 模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用
- 与 ifstream 关联的文件默认以 in 模式打开
- 与 ofstream 关联的文件默认以 out 模式打开
- 与 fstream 关联的文件默认以 in 和 out 模式打开
以 out 模式打开文件会丢弃已有的数据
| |
| ofstream out("file1"); |
| ofstream out2("file1", ofstream::out); |
| ofstream out3("file1", ofstream::out | ofstream::trunc); |
| |
| ofstream app("file2", ofstream::app); |
| ofstream app2("file2", ofstream::out | ofstream::app); |
每次调用 open 时都会确定文件模式
- 在每次打开文件时,都要设置文件模式,可能是显式地设置,也可能是隐式地设置。当程序未指定模式时,就是用默认值。
| ofstream out; |
| out.open("scratchpad"); |
| out.close(); |
| out.open("precious", ofstream::app); |
| out.close(); |
string 流
- istringstream 从 string 读取数据
- ostringstream 向 string 写入数据
- stringstream 既可以 从 string 读取数据也可向 string 写入数据
| sstream strm; strm 是一个未绑定的 stringstream 对象。sstream 是头文件 sstream 中定义的一个类型 |
| sstream strm(s); strm 是一个 sstream 对象,保存 string s 的一个拷贝。此构造函数是 explicit 的 |
| strm.str() 返回 strm 所保存的 string 的拷贝 |
| strm.str(s) 将 string s 拷贝到 strm 中。返回 void |
使用 istringstream
| struct PersonInfo { |
| string name; |
| vector<string> phones; |
| }; |
| |
| int main(int argc, char *argv[]) { |
| |
| string line, word; |
| vector<PersonInfo> people; |
| |
| while (getline(cin, line)) { |
| PersonInfo info; |
| istringstream record(line); |
| record >> info.name; |
| while (record >> word) { |
| info.phones.push_back(word); |
| } |
| people.push_back(info); |
| } |
| |
| return 0; |
| } |
使用 ostringstream
| for (const auto &entry : people) { |
| ostringstream formatted, badNums; |
| for (const auto &nums : entry.phones) { |
| if (!valid(nums)) { |
| badNums << " " << nums; |
| } else { |
| formatted << " " << format(nums); |
| } |
| } |
| if (badNums.str().empty()) os << entry.name << " " << formatted.str() << endl; |
| else cerr << "input error: " << entry.name << " invalid number(s) " << badNums.str() << endl; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!