ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Excel>bytes导表方案

Excel>bytes导表方案

为什么对于Excel表格,要用紧密二进制,不用json?

对于重复相同结构的列表、字典,字段名会重复很多次,多占用大量空间,特别是字段名较长时。如果不是相同数据结构大量重复,比如键值对配置,就适合json。

以及次要原因,{}、""、[]、:这些符号占用空间。

Excel导bytes还有两种转换路线:

字符串>具体类型>byte[]

一种是单元格字符串转换成C#对象:int、float、bool、int[]、string[]、Vector3,再把对象转换成byte[],BinaryWriter提供了把很多基本类型写入字节流的方法,但是没有各种数组、Vector3、字典转换byte[]的方法,要自己定义自己写字符串>数据类型>byte[]。运行时解析时把byte[]解析成各种对象,解析byte[]>对象还要自己写。

有一些框架是用扩展方法给数组写转换、解析。

字符串>byte[]

另一种是简单粗暴的把Excel单元格字符串用BinaryWriter写入byte[],运行时读取时还原字符串,从字符串>对象。这个转换要手写。要注意的是表格里的空单元格在bytes里要占位,避免把一个字符串解析成错误的列。这里看BinaryWriter.Write()写入空字符串,是只写入一个长度头。

using(MemoryStream ms = new MemoryStream()) using(BinaryWriter writer = new BinaryWriter(ms)) { writer.Write(string.Empty); Debug.Log(ms.ToArray().Length); Debug.Log(BitConverter.ToString(ms.ToArray())); }

这个方案要注意即使是int、bool、float这些基本类型,也是写入了字符串,解析时必须先读回字符串,再Parse解析。那么这样又比直接写基本类型麻烦。

并且不能整个文件就一堆字符串byte[],必须有办法分辨一行,以及一共几行。分辨这些就是两种方案,要么加长度头,要么加结束符。那么在文件头加一个行数,告诉循环读取多少次。每一行加\n的话,那个是给字符串分割很方便,BinaryReader识别\n反而不方便。每一行开头记录列数也没必要,因为每一列是要填入对象字段的,不用循环写,结果就是不需要加任何行分割符,就靠固定次数的读取字符串保证正确。

混合方案

那么我们发现,对于int、float、bool,BinaryWriter有转换方法,直接转换byte[]、解析更简单,对于List、Dictionary直接字符串转byte[],解析时转回字符串再解析,手写代码更少,不用再写字符串>List、List>byte[]、byte[]>List。

那么我们能不能把二者结合起来?int、float、bool直接写,其他用字符串。这样需要转换时判断类型行是不是int、float、bool,但又没有必要每行判断一次,每列的类型是固定的。如果不追求性能可以就每行判断一次。如果非要避免每行判断就要用多态,给int、float、bool、string做Converter,开始转换前遍历类型行,得到一个Converter[]。

关于BinaryWriter.Write(字符串)

默认是UTF8编码,在前面写一个字节记录内容的字节数。

单纯UTF8编码、BinaryWriter.Write写入的内容对比:

顺序匹配二进制的缺点

删字段、换字段顺序,解析代码要跟着改,

EndOfStreamException: Unable to read beyond the end of the stream

读到第一行最后一列遇到这个。

FormatException: Too many bytes in what should have been a 7 bit encoded Int32.

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进