本文介绍文件输出流相关的成员函数,包括用法和示例。
目录
- [1 概述](#1 概述)
- [2 输出流的 open 成员函数](#2 输出流的 open 成员函数)
-
- [2.1 新建文件](#2.1 新建文件)
- [2.2 末尾追加](#2.2 末尾追加)
- [2.3 复用流对象](#2.3 复用流对象)
- [2.4 说明](#2.4 说明)
- [3 put 函数](#3 put 函数)
- [4 write 函数](#4 write 函数)
- [5 seekp 与 tellp 函数](#5 seekp 与 tellp 函数)
- [6 输出流的 close 成员函数](#6 输出流的 close 成员函数)
- [7 错误处理函数](#7 错误处理函数)
1 概述
文件输出流的成员函数分为三类:
- 功能等同于流操纵符的成员函数;
- 执行无格式原始写入操作的成员函数;
- 用于修改流状态、不存在对应操纵符与插入运算符的成员函数;
处理顺序式、带格式的文本输出时,仅使用插入运算符 << 和各类操纵符就足够。若要对磁盘二进制文件执行随机读写,就需要使用其他专用成员函数,这类函数可以搭配插入运算符使用,也可以单独调用。
2 输出流的 open 成员函数
想要使用文件输出流 ofstream,必须在构造函数或者 open 函数中将流与某个磁盘文件绑定。如果使用 open 函数,同一个流对象可以先后绑定
多个不同文件。两种绑定方式所传入的文件相关参数完全一致。打开输出流关联的文件时,通常需要
指定打开模式标志。这些标志是定义在 ios 类中的枚举常量,可通过按位或运算符 |组合使用。
下面介绍输出流中三个最常用的场景,对应不同打开模式:
2.1 新建文件
新建文件,若文件已存在则直接删除原有内容:
cpp
ofstream ofile("FILENAME");
// Default is ios::out
ofstream ofile("FILENAME", ios::out);
// Equivalent to above
2.2 末尾追加
向已有文件末尾追加内容,文件不存在则自动创建:
cpp
ofstream ofile("FILENAME", ios::app);
2.3 复用流对象
同一个流先后打开多个文件:
cpp
ofstream ofile();
ofile.open("FILE1", ios::in);
// Do some output
ofile.close(); // FILE1 closed
ofile.open("FILE2", ios::in);
// Do some more output
ofile.close(); // FILE2 closed
// When ofile goes out of scope it is destroyed.
2.4 说明
- 默认模式 ios::out
- 只写模式,打开文件时直接清空原有内容,适合覆盖重写;
- ios::app 追加模式
- 所有写入操作强制在文件尾部执行,不会覆盖原有数据;
- 复用流对象流程
- open() 绑定文件→文件IO→ close() 释放文件句柄→再次 open() 绑定新文件;
- RALL 自动关闭
- 对象离开作用域析构时,会
自动调用 close(),无需手动关闭;- 模式组合示例
- ios::out | ios::binary:二进制覆盖写入;
- ios::app | ios::binary:二进制追加写入;
3 put 函数
put 函数将一个字符写入到输出流。默认情况下,以下两个语句相同,但第二个受流的格式化参数的影响:
cpp
cout.put('A');
// Exactly one character written
cout <<'A'; // Format arguments 'width' and 'fill' apply
说明
- cout.put(ch)
- 属于无格式原始输出函数,
完全无视setw、对齐、填充字符这类格式化配置,只原样输出这一个字符,不会补充任何填充空位。- cout << ch
- 是
格式化插入运算符,会先检查当前流的宽度、填充规则;- 如果设置了输出宽度,会自动在字符前后补填充符;
4 write 函数
write 函数将一块内存数据写入文件输出流,长度参数用于指定要写入的字节总数。下面示例创建文件输出流,并把 Date 结构体的二进制原始数据写入文件:
cpp
// write_function.cpp
// compile with: /EHsc
#include <fstream>
using namespace std;
struct Date
{
int mo, da, yr;
};
int main()
{
Date dt = { 6, 10, 92 };
ofstream tfile("date.txt", ios::binary);
tfile.write((char*)&dt, sizeof dt);
}
说明
- 核心特性:
- write 遇到空字符 \0 不会停止写入,
能完整写出整个结构体内存块;- write 属于无格式二进制写入,完全不触发任何格式化逻辑,不会截断于 \0,
适合结构体、数组等整块内存持久化。- 参数解析:
- 第一个参数:
内存起始地址,必须转为 char*;- 第二个参数:
总字节数,通常用 sizeof(结构体/变量) 获取;- 强制要求二进制模式 ios::binary:
- 如果不开启二进制标志,文本模式会自动转换换行符,破坏结构体二进制数据,读取时会解析出错。
- 和 put(ch) 区别
- put(ch):单次只写 1 个字节;
- write(buf, len):一次性写入任意长度连续内存块;
5 seekp 与 tellp 函数
文件输出流内部维护一个写入指针,该指针标记下一段数据将要写入的文件位置。
成员函数 seekp 用于修改这个写入指针,以此实现磁盘文件的随机写入功能;
成员函数 tellp 用来获取当前写入指针所处的文件偏移位置;
输入流中存在功能对应的 seekg、tellg 函数;
命名含义:p = put,代表写入,仅用于 ofstream / fstream;
g = get,代表读取,仅用于 ifstream / fstream;
作用:tellp():返回当前写入位置的字节偏移量(文件流位置类型 streampos);
seekp(偏移量, 基准位置):跳转写入指针,支持三种基准:
- ios::beg:文件开头
- ios::cur:当前指针位置
- ios::end:文件末尾
使用场景 :二进制文件局部覆盖写入,跳过指定长度、修改文件中间某段数据。必须搭配 ios::binary 打开,文本模式下换行符转换会导致偏移计算错乱。示例代码如下所示:
cpp
// write_function.cpp
// compile with: /EHsc
#include <fstream>
using namespace std;
int main()
{
ofstream file("data.bin", ios::out | ios::binary);
int num = 123;
// 写入4字节整数
file.write((char*)&num, sizeof(int));
// 获取当前写入位置
streampos pos = file.tellp();
// 跳回文件开头,覆盖重写
file.seekp(0, ios::beg);
int newNum = 456;
file.write((char*)&newNum, sizeof(int));
}
6 输出流的 close 成员函数
close 成员函数关闭与文件输出流绑定的磁盘文件。必须关闭文件,缓冲区所有待写入的数据才会完整落地到磁盘。
- 必要时 ofstream 的析构函数会自动帮你关闭文件;但如果你需要用同一个流对象打开另一个文件,就必须手动调用 close。
- 仅当文件是通过流的构造函数或 open 成员函数打开时,输出流析构函数才会自动关闭文件。
- 如果你在构造函数传入一个已经打开的文件描述符,或是使用 attach 成员函数绑定外部文件,此时必须显式调用 close 关闭文件,析构不会自动处理。
调用 close() 会强制刷新缓冲区,把内存中缓存的写入数据全部写入磁盘,再释放文件句柄。
7 错误处理函数
写入流时,可使用以下成员函数检测错误状态:
| 函数 | 返回值说明 |
|---|---|
| bad | 发生不可恢复的严重错误时返回 true |
| fail | 出现不可恢复错误,或是常规预期故障(类型转换失败,文件不存在等)时返回 true;调用 clear(0) 清除状态后通常可继续执行 IO |
| good | 无任何错误标记、且未设置文件结束标记时返回 true |
| eof | 读到文件末尾时返回 true |
| clear | 修改流内部错误状态;无参默认调用会清空所有错误标记 |
| rdstate | 返回当前完整的错误状态掩码 |
- 流重载了 ! 运算符,功能等价于 fail():
cpp
if(!cout)
// 等价于
if(cout.fail())
- 同时重载了 void* 转换运算符,逻辑与 ! 相反:
- void* 转换判断不等同于 good(),因为它不会校验文件结束标记 eof。
cpp
if(cout)
// 等价于
if(!cout.fail())
说明
- 错误标记层级关系 :
- badbit:硬件/底层致命错误,bad() 单独检测该位;
- failbit:转换、打开文件等常规失败,fail() 同时检测 badbit+failbit;
- eofbit:仅文件末尾标记,good() 要求 badbit、failbit、eofbit 全部置0;
- 三种判断方式区别 :
- if(stream):仅判断 !fail(),忽略 eof 状态;
- if(!stream):等同于 stream.fail();
- if(stream.good()):最严格,无错误且未到文件末尾。
- 错误恢复流程 :
- 流一旦触发 fail / bad。后续所有读写操作都会静默失效;
- 调用 stream.clear() 清空错误标记后,才能恢复正常 IO;