绝大多数情况下不该自己设计二进制文件格式;仅当需极致读写性能、结构长期固定、跨平台兼容不重要或须规避json/xml开销时才考虑,且须规范头信息、紧凑struct布局、避免binaryformatter。

二进制文件该不该自己设计
绝大多数情况下,不该。除非你明确需要:跨平台兼容性不重要、读写性能压倒一切、数据结构高度固定且长期不变、或者必须规避 JSON/Xml 的解析开销和体积膨胀。
常见误判是“为了安全”或“防止用户修改”——二进制本身不等于加密,也拦不住有心人;真正防篡改得靠签名,防读取得靠加密,不是换种序列化格式就能解决。
-
BinaryFormatter已被标记为过时且不安全,绝对不要用 - 用
System.Text.Json或MessagePack通常比手写二进制快得多,且更易维护 - 如果只是想压缩体积,先试
MessagePackSerializerOptions的CompatibilityLevel和UseDateTimeEncoding
头信息怎么写才不容易翻车
自定义二进制格式失败,八成栽在头信息设计上:没魔数、没版本、没长度字段,导致后续任何变更都变成不兼容升级。
一个最小可用头至少包含 3 个字段,按顺序写死(推荐 Span<byte></byte> + BinaryPrimitives 写):
- 4 字节魔数,比如
0x4642494E("FBIN" ASCII 码),用于快速识别文件类型 - 2 字节主版本号 + 2 字节次版本号(
ushort),后续加字段只升次版本,改语义才升主版本 - 4 字节总数据长度(不含头),方便校验截断或读取越界
别把头做成类然后 MemoryMarshal.AsBytes —— 字段顺序、填充、大小端会悄悄搞垮你。直接用 BinaryPrimitives.WriteUInt32BigEndian 这类函数逐字段写。
Shell本身是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。作为命令语言,它交互式地解释和执行用户输入的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。它虽然不是Linux系统核心的一部分,但它调用了系统核心的大部分功能来执行程序、建立文件并以并行的方式协调各个程序的运行。因此,对于用户来说,shell是最重要的实用程序,深入了解和熟练掌握shell的特性极其使用方法,是用好Linux系统
如何避免 struct 序列化时的内存对齐陷阱
C# 的 struct 默认按 CPU 对齐(比如 long 在 64 位系统对齐到 8 字节),但文件格式要求的是**紧凑布局**,否则写出来的字节流中间全是 padding,跨平台读取直接错位。
- 必须加
[StructLayout(LayoutKind.Sequential, Pack = 1)],Pack = 1是关键,禁用所有填充 - 字段顺序要按字节大小从大到小排(
long→int→short→byte),否则即使Pack = 1也可能因编译器优化出意外 - 字符串不能直接放 struct 里,要用
fixed byte name[32]或单独存偏移+长度,否则引用类型破坏二进制确定性
验证方法:用 Unsafe.SizeOf<myheader>()</myheader> 检查是否等于你手动算的字节数;再用 MemoryMarshal.AsBytes 转成 byte[] 打印前 16 字节,看是否符合预期。
读写性能瓶颈往往不在序列化本身
实测中,90% 的“慢”来自 IO 层:反复 FileStream.Read 小块数据、没用 Span<byte></byte> 复用缓冲区、或把整个大文件读进 byte[] 再解析。
- 写入时用
FileStream构造时加FileOptions.SequentialScan,提示 OS 预读 - 读取固定结构头:用
Span<byte></byte>直接指向文件开头 16 字节,调BinaryPrimitives.ReadUInt32LittleEndian - 解析主体数据前,先用
fileStream.Position = header.DataOffset跳转,别从头扫 - 避免
BinaryReader—— 它内部带缓冲且无法控制,小数据反而更慢;纯Span+BinaryPrimitives才是零分配关键
最常被忽略的一点:如果你的“高效”是指毫秒级加载几百 MB 数据,那真正卡住的往往不是反序列化,而是磁盘寻道或 SSD 闪存页映射延迟——这时候该考虑内存映射 MemoryMappedFile,而不是继续抠 struct 字节序。









