本文介绍一下二进制输出文件的规则及其作用。
目录
- [1 概述](#1 概述)
- [2 二进制输出](#2 二进制输出)
- [3 示例](#3 示例)
- [4 说明](#4 说明)
1 概述
流最初是为文本处理设计的,因此文件输出默认采用文本模式 。
在文本模式下,写入时单个换行符 \n 会自动扩展为 Windows 系统的回车 + 换行双字符 \r\n。这种自动转换会破坏二进制数据,示例如下:
cpp
// binary_output_files.cpp
// compile with: /EHsc
#include <fstream>
using namespace std;
int iarray[2] = { 99, 10 };
int main( )
{
ofstream os( "test.dat" );
os.write( (char *) iarray, sizeof( iarray ) );
}
开发者预期输出字节序列:{99,0,10,0};
但文本模式下实际写入:{99,0,13,10,0},多插入了回车符 \r,后续读取二进制数据的程序会解析出错。
2 二进制输出
如果需要纯粹的二进制输出、字节原样写入不做任何字符转换,构造 ofstream 时传入
ios_base::binary打开模式标志:
cpp
// binary_output_files2.cpp
// 编译参数:/EHsc
#include <fstream>
using namespace std;
int iarray[2] = { 99, 10 };
int main()
{
ofstream ofs ( "test.dat", ios_base::binary );
// 精确写入8个字节,无任何字符转译
ofs.write( (char*)&iarray[0], sizeof(int)*2 );
}
3 示例
下述示例分别通过文本模式和二进制模式写入,然后二进制模式读取查看区别,文本模式读取查看区别:
cpp
// binary_output_files.cpp
// 编译参数:/EHsc
#include <fstream>
#include <iostream>
using namespace std;
int iarray[2] = { 99, 10 };
int main()
{
//文本模式写入(无ios::binary)
{
ofstream os("test.dat");
os.write((char*)iarray, sizeof(iarray));
os.close();
//二进制读取
ifstream is("test.dat", ios::binary);
char buf[16] = { 0 };
is.read(buf, sizeof(buf));
is.close();
//逐字节打印十六进制,观察多出0x0D(13)
cout << "文件内字节(十六进制):" << endl;
for (int i = 0; i < sizeof(iarray) + 2; i++)
{
printf("%02X ", (unsigned char)buf[i]);
}
cout << endl;
//文本模式读取
int readArr[2] = { 0 };
ifstream textIn("test.dat");
textIn.read((char*)readArr, sizeof(readArr));
textIn.close();
cout << "预期数组:[99, 10]" << endl;
cout << "文本模式读回:[" << readArr[0] << ", " << readArr[1] << "]" << endl;
}
//二进制模式写入(ios::binary)
{
ofstream os("test2.dat", ios::binary);
os.write((char*)iarray, sizeof(iarray));
os.close();
//二进制读取
ifstream is("test2.dat", ios::binary);
char buf[16] = { 0 };
is.read(buf, sizeof(buf));
is.close();
//逐字节打印十六进制,观察多出0x0D(13)
cout << "文件内字节(十六进制):" << endl;
for (int i = 0; i < sizeof(iarray) + 2; i++)
{
printf("%02X ", (unsigned char)buf[i]);
}
cout << endl;
//文本模式读取
int readArr[2] = { 0 };
ifstream textIn("test2.dat");
textIn.read((char*)readArr, sizeof(readArr));
textIn.close();
cout << "预期数组:[99, 10]" << endl;
cout << "文本模式读回:[" << readArr[0] << ", " << readArr[1] << "]" << endl;
}
return 0;
}
运行结果:
很明显,二进制读取的时候是有问题的,但是文本模式写入二进制模式读取本身逻辑上就不通畅,所以什么方式写入什么方式读取不就好了。
4 说明
- 文本模式转换规则(仅 Windows 平台生效)
- 写入:内存中的 \n (0x0A)自动变成 \r\n (0x0D 0x0A)存入文件;
- 读取:文件里连续的 \r\n 自动还原为单个 \n;
- Linux / macOS 无此转换,文本、二进制模式行为一致;
- 二进制模式核心作用
- 添加 ios_base::binary 后,所有字节原样落地,不会插入、替换任何换行相关字符,适合结构体、数组、图片、程序等二进制数据持久化。
- 风险场景
- 使用 write()
序列化数字、结构体时,必须加 binary,否则数值中出现 0x0A 就会多出 0x0D,字节长度错乱,读取数据完全失真。
