如何实现一个表格 - Microsoft Excel

1,632 阅读5分钟

要实现表格,当然要先分析Microsoft Excel。下文是从我的角度,一步步对excel的数据结构进行分析。

如何用nodejs导出excel文件

excel对于我来说是一个黑盒,要想分析excel的数据结构,首先要了解那些开源的工具库是如何生成excel文件的。

选择excel-export这个npm包作为入口,进行分析。通过官方文档得知,主要通过以下函数实现的生成excel

var nodeExcel = require('excel-export');
// ...中间不重要的代码
nodeExcel.execute(conf)

通过查看excel-export源码,可以得知excel文件其实是通过zip格式的压缩包,精简代码如下:

require('node-zip');
// ...
exports.execute = function(config) {
    var xlsx = new JSZip(templateXLSX, {
        base64: true,
        checkCRC32: false
    });
    // ...
    var results = xlsx.generate({
        base64: false,
        compression: "DEFLATE"
    });
    // ...
    return results;
}

这样就简单多了,我们之后进行分析就可以直接新建一个Excel文档,按zip格式进行解压,对解压后的文件进行分析。

提示:可以直接修改excel的后缀名xlsx为zip,然后使用解压工具进行解压。

备注:如果要导出excel,直接使用相关的库就可以了,so easy。

原始excel数据

sheet1: 作文

sheet2: 数学

分析解压后的文件

解压后的目录结构如下(不同版本可能有些差别):

.
├── [Content_Types].xml
├── _rels
├── docProps
│   ├── app.xml
│   └── core.xml
└── xl
    ├── _rels
    │   └── workbook.xml.rels
    ├── sharedStrings.xml
    ├── styles.xml
    ├── theme
    │   └── theme1.xml
    ├── workbook.xml
    └── worksheets
        ├── sheet1.xml
        └── sheet2.xml

相关文件夹和文件的作用,可以通过文件名大致猜测出来。

核心:表格数据是怎么储存的

通过名称可以猜出来,xl/worksheets/sheet1.xmlxl/worksheets/sheet2.xml中存的是表格元数据。以下是sheet1.xml的内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" 
    xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac xr xr2 xr3" 
    xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" 
    xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" 
    xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" 
    xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xr:uid="{F84174FE-05C3-E94C-9973-FAF4BA0FB974}">
    <dimension ref="A1:S13"/>
    <sheetViews>
        <sheetView tabSelected="1" topLeftCell="M1" workbookViewId="0">
            <selection activeCell="AD11" sqref="AD11"/>
        </sheetView>
    </sheetViews>
    <sheetFormatPr baseColWidth="10" defaultRowHeight="16"/>
    <cols>
        <col min="3" max="3" width="32.33203125" customWidth="1"/>
    </cols>
    <sheetData>
        <row r="1" spans="1:19" s="1" customFormat="1" ht="41" customHeight="1">
            <c r="A1" s="1" t="s">
                <v>0</v>
            </c>
            <c r="B1" s="1" t="s">
                <v>1</v>
            </c>
            <c r="C1" s="1" t="s">
                <v>4</v>
            </c>
        </row>
        <row r="2" spans="1:19">
            <c r="A2" t="s">
                <v>2</v>
            </c>
            <c r="B2" t="s">
                <v>3</v>
            </c>
            <c r="C2">
                <v>1</v>
            </c>
        </row>
        <row r="3" spans="1:19">
            <c r="A3" t="s">
                <v>5</v>
            </c>
            <c r="B3" t="s">
                <v>6</v>
            </c>
            <c r="C3">
                <v>4</v>
            </c>
        </row>
        <row r="13" spans="1:19">
            <c r="R13" t="s">
                <v>8</v>
            </c>
            <c r="S13">
                <f>SUM(C2:C3)</f>
                <v>5</v>
            </c>
        </row>
    </sheetData>
    <phoneticPr fontId="1" type="noConversion"/>
    <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
</worksheet>

首先,很开心,数据结构很清楚,大致与html的table、tr、td的结构相似,并且通过row上面的spans来标示每行的数据起始位置。 可以得出以下几点:

  • 数据结构很清除,大致与HTML中的table、tr、td的结构相似,只不过使用的是sheetData、row、c
  • 通过row的spans属性来判断每行的起始位置
  • 通过row的r属性可以判断出来是第几行,并且需要注意起始行是1,不是程序里面的0
  • 通过c的r属性可以判断单元格在第几行第几列
  • 样式属性通过s属性进行关联
  • 通过dimension可以知道表格数据所占据的总大小
  • 通过sheetViews可以知道保存时,选中的单元格的位置,打开的表格
  • 自定义行高的时候,会在row上多一个ht属性,并且customHeight="1"
  • 自定义列宽的时候,会多一个cols标签,通过其中col来定义自定义的宽度,col中的min和max标示起始列,width标示自定义的宽度
  • 通过pageMargins属性设置页面属性

但是,需要注意一点,文本内容都没包含在表格中,都是储存在xl/sharedStrings.xml中,然后在sheet中通过索引进行引用,xl/sharedStrings.xml内容如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="12" uniqueCount="10">
    <si>
        <t>标题</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>姓名</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>你好,未来</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>张三</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>得分</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>明天</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>里斯</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>王五</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>总计</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
    <si>
        <t>时间</t>
        <phoneticPr fontId="1" type="noConversion"/>
    </si>
</sst>

最后,也是最重要的,大家不要这么盲测,因为我们有ECMA大法:Office Open XML File Formats:www.ecma-international.org/publication…