由于要parse老版本的*.xls文档,看了一下xls的格式,发现xls中存在一个极其奇葩的设计。
xls是Excel 97/95版本的,这种文件是二进制格式,带有一个文件头,文件头指向后面一系列的记录。
每个记录是T-L-V(Type-Length-Value)结构的,V里面会有很多结构,这个没啥问题。
由于每个记录有最大长度限制(Excel 97的记录的L最大值是8224字节,Excel 95是2080字节),
所以MS的人又搞了一个CONTINUE类型的记录,
一个超长的数据,会拆分为一个头部记录,外加一个或多个CONTINUE类型的记录。
这个拆分记录的设计,奇葩在什么地方?
一、在头部记录中,没有标志标明后面是否跟的有CONTINUE记录。
这样的话,拼凑这些记录的办法有:
1、一直向后预读,看是否存在CONTINUE记录,有则全部读进来拼成一块数据再解析。
流式解析的话,预读会降低效率,除非把预读的缓存着,而缓存后面的记录会增加复杂度。
但MS的人又搞了一个设计,导致你无法简单拼接记录。这个设计就是:
如果记录中的某个结构是字符串数据,这个结构将包含一个固定的头,用来表明字符串的长度、字符宽度等信息,固定头后面是变长的字符串数据。
如果要拆分记录时,正好是要从这个字符串结构这里拆开的话,它规定的拆分规则是:字符串的第一个字符一定要和其固定头保持在同一个记录中。
而且拆了字符串后,这个字符串处于下一个记录(CONTINUE记录)中的字符宽度是可以变的,比如8-bit的windows 1252编码的字符变为16-bit的utf16/GBK字符,或者反过来。虽然一般并不变。
也就是说被拆为两半的字符串,这两半的编码可以不一样!在CONTINUE记录的数据区的第一个字节,就是用来表明字符串的后一半的字符宽度的。但是就是因为这个字节的存在,导致无法简单拼接记录。因为如果不是从字符串结构的中间来拆分记录的话,不存在这个(重复的)字节。这个字节有时有,有时没有,那就难以简单处理。
2、先尝试解析前面的记录,直到发现必须要有CONTINUE记录才能满足解析要求,那么就记下中间状态,持续读取CONTINUE记录,根据中间状态继续解析,或者从头重试解析。
总的来说,因为这个奇葩设计,导致parser的复杂度大大增加。
--
FROM 125.35.123.*