应先校验剩余字节数再读取,避免EndOfStreamException;写入时需统一字节序、显式编码字符串、标记结构体布局、及时Flush;BinaryReader/Writers须与FileStream正确配合并用using管理生命周期。

BinaryReader 读取时抛出 EndOfStreamException 怎么办
根本原因不是文件损坏,而是你没控制好读取边界——BinaryReader 不会自己判断“该停了”,它只信你给的指令。比如用 ReadInt32() 却只剩 2 个字节,立刻炸。
- 先用
BaseStream.Length和BaseStream.Position算剩余字节数,再决定能否读完整类型(int要 4 字节,double要 8 字节) - 别依赖
PeekChar(),它对二进制流无效;BaseStream.CanSeek为false时(如网络流),Length可能不可用,得换方案(如协议头声明长度) - 常见翻车点:循环里无条件调
ReadByte(),直到异常才退出——这属于把异常当流程控制,性能差还掩盖真实问题
写入二进制文件时数据错位或乱码
错位 = 字节序没对齐,乱码 = 把字符串当 raw bytes 直接写。C# 默认用 UTF-16 写字符串,但多数二进制协议要求 UTF-8 或固定长度字节数组。
- 写字符串别直接用
Write(string),改用Encoding.UTF8.GetBytes(str)+Write(byte[]),明确控制编码和长度 - 结构体写入前必须加
[StructLayout(LayoutKind.Sequential, Pack = 1)],否则字段间可能有填充字节,读出来就偏移 - 写完记得调
Flush(),尤其用BufferedStream包裹时,不 flush 可能丢最后几 KB
BinaryReader / BinaryWriter 和 FileStream 的配合要点
它们只是流的“翻译器”,底层全靠 FileStream。开错模式、没关流、共享同一个流实例,都会让读写互相污染。
-
FileStream必须用FileMode.OpenOrCreate+FileAccess.ReadWrite才能同时读写;只开Read模式却拿去构造BinaryWriter,运行时报NotSupportedException - 不要手动调
FileStream.Close()后继续用关联的BinaryReader——它内部的BaseStream已失效,后续读操作抛ObjectDisposedException - 推荐用
using块嵌套:using var fs = new FileStream(...); using var br = new BinaryReader(fs);,确保顺序释放
替代方案:MemoryStream + Span 更快吗
纯内存操作时,Span 配 BinaryPrimitives 确实更快且零分配,但前提是数据已在内存。如果文件上百 MB,全读进内存再解析,反而 OOM。
- 小文件(MemoryStream +
BinaryPrimitives.ReadInt32BigEndian(ReadOnlySpan能省掉) BinaryReader的虚方法调用和缓冲区管理开销 - 大文件或流式处理(如日志分块读),坚持用
FileStream+BinaryReader,注意设置bufferSize参数(默认 4096,IO 密集型可提到 65536) -
BinaryReader构造函数里传leaveOpen: true,避免它自动关掉底层流——这点很多人漏设,导致后续无法复用流
二进制读写最麻烦的从来不是语法,而是你脑子里有没有时刻绷着“字节层面”的那根弦:每个 ReadXxx() 调用背后是几个字节被挪走,每个 WriteXxx() 输出是否和协议文档里的字节布局完全一致。差一个字节,整段数据就废。










