Excel文件格式解析

这篇介绍一下Excel文件的格式,额。。。为啥会研究这个呢,因为公司项目有个模块需要读取Excel文件,完成了顺便把相关资料整理一下,以下内容均来自互联网~

微软的Excel格式有两种:.xls和.xlsx。Excel2003及之前的版本是.xls格式,从Excel2007开始支持.xlsx格式。只使用基本功能的话两种格式看起来并没有多大差别,但是两者文件格式确实截然不同的。

excel97-2003(xls格式)

XLS文件最为一个文件流(FileStream),是由多个子流(SubStream)构成的,下面这张图是将各个SubStream按顺序依次展现出来,这些SubStream都是有若干个整数倍Block(1 block = 512 bytes)组成。

SubStream
XLS Header 占1个block(512bytes)固定大小
WorkBook 最小占8个block(4096bytes)
Summary Information 占8个block(4096bytes)固定大小
Document Summary Information 占8个block(4096bytes)固定大小
XLS Header扩展区 只有.xls文件超过一定大小时,才会出现
BigBolck pointer 最小占1个(512bytes)
RootEntry 占1个block(512bytes)

其中所有单元格数据记录在WorkBook子流中,以下为WorkBook区的重要内容结构。

0x0809 WorkbookHeader,即BOF,长度范围1~20

offset size content
0 2 存放XLS文件的版本类型 BIFF8/BIFF8X 0x0600 BIFF7 0x0500
2 2 WorkBookGlobals,0x0005

0x0031 FONT,长度24bytes。0x041EFORMAT

0x00E0 XF ExtendFORMAT

0x0293 STYLE

0x0085 BOUNDSHEET,即Sheet指针区,N个Sheet则有N个0x0085,包含每个Sheet的名称、sheet数据内容在xls文件中的偏移量。

offset size content
0 2 数据段长度(Sheet指针区和名称区长度)
2 4 该Sheet的BOF的绝对位移量,相对于Workbook
6 Sheet名称区,长度为数据段长度-2

0x008C Country,语言和地区设置。

0x00FC SST内容(SharingStringTable用来存放字符串,目的是为了让各个sheet都能够共享该SST中字符串内容)(Excel表数据,所有Sheet数据均放于此)

offset size content
0 4 数据中所有字符串的数量
4 4 将要放在SST中字符串的数量
以下开始针对每个字符串进行循环处理
8 2 该字符串的长度,若为双字节字符也算做一个
10 1 字符串中若包含双字节字符(如中文),则为0x01,若为纯ASCII字符,则为0x00
11 n 存放字符串信息

0x00FF ExtendedSST内容 在读取XLS文件时显得不重要,但当创建XLS文件时却非常重要

offset size content
0 2 每一区块字符串的个数,其值>=8
以下开始针对每一区块字符串进行循环处理
2 4 每一区块第一个字符的绝对位置
6 2 每一区块第一个字符的相对位置
8 2 保留

0x0809 Sheet1内容

0x0809 Header,20bytes

0x020BIndex,16+4*N个字节

(注:注意标志后有两位Size位,要从Size位计起)

offset size content
0 4 保留
4 4 第一行的序号
8 4 最后一行的序号(OpenOffice上介绍是sheet结尾的第一个未使用行的序号,行数的基数按0开始)
10 4 指向DEFCOLWIDTH(0x0055)相对于Workbook Header的位置
14 4*N 指向DBCELL(0x00D7)相对于WorkbookHeader的位置,当存在多个DBCELL时,需要设置多个。N为该sheet的行数/32

0x0055 DEFCOLWIDTH,6bytes

0x0200 Dimensions,18byte,存放sheet1的行数和列数。

offset size content
0 2 段大小(下列数据段大小)
2 4 第一行的序号
6 4 最后一行的序号
10 2 第一列的序号
12 2 最后一列的序号
12 2 保留
RowBlock

0x0208 Row,20bytes,Sheet中有多少行就有对应多少个0x0208开头的行信息,如果sheet中有5行,那么RowBlock的大小就是20*5bytes

offset size content
0 2 行的序号
2 2 列的序号
CellBlock

设置sheet中每一个单元格的信息,顺序为针对每一行的每一列依次设置,如00,01,02……10,11,12……20,21,22……

0x00FD LABELSST,14bytes,针对字符串值已经在SST中保存,这里只保存其对应的序号。记录文字串(在共享资料表裡)的Cell,格式为:
2byte:第几行(由0编起)
2byte:第几列(由0编起)
2byte:XFrecord索引值
4byte:共享字串表的索引值

0x027E RK2,14bytes,针对RK值(指编过码的整型或浮点型值)

0x00BD MULRK,28bytes记录数字(多个连续列)的Cell,格式为:
2byte:第几行(由0编起)
2byte:起始列(由0编起)
?byte:RKCell资讯(每个6byte)
2byte:XFrecord索引值
4byte:RK数值(后述)
2byte:结束列(由0编起)

0x00D7 DBCELL,随着行数的变化而发生变化

offset size content
0 4 DBCELL的偏移量减去RowBlock中第一个Row的偏移量
4 2 CellBlock中首行的第一个Cell的偏移量减去RowBlock中第二个Row的偏移量
以下为循环处理部分,1<n<=Max_Row
6 2 CellBlock中第n行的第一个Cell的偏移量减去第n-1行的第一个Cell的偏移量

当XLS中的行数大于32时,系统将循环处理,对于每个32行内容单独生成RowBlock、CellBlock和DBCELL。

0x0809 Sheet2内容

0x0809 Sheet3内容(如果有Sheet2、Sheet3)

0x000A EOF,Workbook结束标示符

==========这是吐槽线===========
这格式确实已经落后了,实在是太复杂。。。如果可以,我不再想碰这。

excel2007(xlsx格式)

xlsx格式相比旧的xls格式的数据结构要简单的多,其使用xml格式进行存储。

如下的表格:

A B C
1 姓名 性别 年龄
2 张三 30
3 小明 16
4 小红 20

然后将xlsx文件后缀改为zip,用压缩软件打开,其内容存储在sheet1.xml文件里:

<sheetData>
    <row r="1" spans="1:3" x14ac:dyDescent="0.2">
        <c r="A1" t="s">
            <v>0</v>
        </c>
        <c r="B1" t="s">
            <v>2</v>
        </c>
        <c r="C1" t="s">
            <v>1</v>
        </c>
    </row>
    <row r="2" spans="1:3" x14ac:dyDescent="0.2">
        <c r="A2" t="s">
            <v>3</v>
        </c>
        <c r="B2" t="s">
            <v>6</v>
        </c>
        <c r="C2">
            <v>30</v>
        </c>
    </row>
    <row r="3" spans="1:3" x14ac:dyDescent="0.2">
        <c r="A3" t="s">
            <v>4</v>
        </c>
        <c r="B3" t="s">
            <v>6</v>
        </c>
        <c r="C3">
            <v>16</v>
        </c>
    </row>
    <row r="4" spans="1:3" x14ac:dyDescent="0.2">
        <c r="A4" t="s">
            <v>5</v>
        </c>
        <c r="B4" t="s">
            <v>7</v>
        </c>
        <c r="C4">
            <v>20</v>
        </c>
    </row>
</sheetData>

<c></c>标签就代表每个单元格,t="s"表示值为字符串,对应的字符串值可以在sharedStrings.xml里找到:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="9" uniqueCount="8">
    <si>
        <t>姓名</t>
    </si>
    <si>
        <t>年龄</t>
    </si>
    <si>
        <t>性别</t>
    </si>
    <si>
        <t>张三</t>
    </si>
    <si>
        <t>小明</t>
    </si>
    <si>
        <t>小红</t>
    </si>
    <si>
        <t>男</t>
    </si>
    <si>
        <t>女</t>
    </si>
</sst>

看新格式多简单。。。