用 BinaryReader
和 BinaryWriter
读写结构化二进制数据最稳妥
这两个类专为 .NET 的二进制序列化设计,自动处理字节序、类型长度和基础编码,比直接操作
Stream更少出错。适用于保存/加载自定义结构体、数值数组、字符串(需注意编码)等。
常见错误:用
BinaryWriter.Write(string)写入后,用
BinaryReader.ReadString()读取失败——因为后者依赖前缀长度(UTF-8 编码的 7 位变长整数),若文件非它所写,会抛
EndOfStreamException或乱码。 写字符串时,如需兼容性更强,改用
Write(byte[])+
Encoding.UTF8.GetBytes()读数值类型(
int、
double等)完全安全,它们按小端序固定长度写入 不要跨平台共享
BinaryReader写出的文件——.NET 不保证其他语言能解析其字符串格式或类型标识
直接用 Stream.Read
/ Stream.Write
控制每个字节
当你需要精确控制布局(比如对接 C/C++ 结构体、网络协议头、图像 raw 数据),必须绕过高层封装,直接操作字节数组。
典型场景:读取 BMP 文件头(14 字节固定结构)、拼接加密后的字节块、解析自定义二进制协议包。
务必检查返回值:Read()可能只读到部分字节,需循环直到填满缓冲区或遇到 EOF 写入前确认
Stream.CanWrite且已定位到正确位置(
Position可写) 使用
MemoryStream做中间缓存时,记得调用
ToArray()获取真实字节,而非
GetBuffer()(后者可能含未使用填充)
Span<byte></byte>
+ BinaryPrimitives
是高性能二进制解析新方案
.NET Core 2.1+ 引入了零分配的二进制解析方式,适合高频、大数据量场景(如日志解析、实时传感器数据流)。
它不依赖流对象,直接在内存切片上解析整数、浮点数,避免了
BinaryReader的内部缓冲和装箱开销。 读取小端整数:
BinaryPrimitives.ReadInt32LittleEndian(data),
data是
Span<byte></byte>写入需手动复制:
BinaryPrimitives.WriteInt32LittleEndian(buffer, value)不处理字符串、变长字段,所有偏移和长度都得自己算清楚——错一位就全盘错解
文件打开模式和编码陷阱必须显式指定
二进制操作最常被忽略的是
FileStream构造参数。默认
FileMode.Open+
FileAccess.Read只读,想写必须显式设
FileAccess.ReadWrite;追加写要用
FileMode.Append并确保光标在末尾。
另一个坑是文本编码混入:哪怕你只读字节,如果用
StreamReader打开二进制文件,它会尝试按 UTF-8 解码,遇到非法字节直接抛异常。 永远用
new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true)显式控制行为 避免把
byte[]误传给
Encoding.GetString()处理非文本数据(比如图片像素) 调试时用
File.ReadAllBytes()快速看前几十字节十六进制,比盲目猜更可靠
二进制操作的核心不是“怎么写”,而是“谁定义了格式”。哪怕代码全对,只要和协议文档或 C 结构体对不上字段顺序、对齐、符号位,数据就不可用。建议先用十六进制编辑器对照验证几组手工构造的数据。
