在 Gopher Meetup 北京站上,阿里巴巴高级开发工程师、前百度 Go 语言编程委员会成员续日进行了主题为《Go语言国际电子表格文档格式标准实践》的演讲。

Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376, ISO/IEC 29500 国际电子表格文档格式标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档,可以应用于各类报表系统中,做为 2018 开源中国最有价值开源项目,目前已成为 Go 语言最受欢迎的 Excel 文档基础库。本次演讲将探究国际文档格式标准,解读使用 Go 语言从 0 到 1 打造 Excelize 背后的故事,分享 Go 语言在国际电子表格文档格式标准方面的实践。

以下为演讲内容整理。

00

前言

大家好!我叫续日。今天给大家带来 Go 语言在国际电子表格文档格式标准实践方面的分享。提起电子表格大家都比较熟悉,以 Excel 为代表的经典电子表格已经应用在各行各业当中,根据相关研究机构的估算数据,世界上 Office 办公文档的数量在 2007 年的时候已经达到了 400 亿规模,并且每年大约以数十亿的规模增长。

为什么要用 Excelize

Excel 文档作为一种数据承载的重要载体,在很多领域都有应用。作为开发者,一些情况下需要用编程的方式操作这些 Excel 文档,比如从已有电⼦表格⽂档中读取内容、创建新的电⼦表格⽂档、基于已有⽂档(模版)⽣成新的电⼦表格⽂档、向电⼦表格⽂档中插⼊图⽚、图表和表格等元素,有时还需要跨平台实现这些操作。Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,使用 Excelize 可以方便的实现上述需求。今天的分享包含两个部分,第一部分介绍国际电子表格文档格式标准的内容,第二部分向大家分享在使用 Go 语言实现标准过程当中的一些经验。

01

文档格式国际标准

如果要用编程的方式操作电子表格文档,首先需要了解其标准。ECMA-376, ISO/IEC 29500 做为国际文档格式标准是一种基于 XML 和 ZIP 技术的文档格式,对应大家所熟悉的 Office 文档文件。这个标准的名字比较长,里面涉及到三个国际的标准化组织。ECMA 是欧洲计算机厂商联盟,ISO 大家比较熟悉,国际标准化组织,IEC 是国际电工协会,这三个国际化组织联合制定了文档格式国际标准。

标准特点分为六个方面,下面解释一下比较有代表性的几个特点。互操作性, 文档格式标准独立于专有格式、功能和运行时环境,平台语言无关,使开发人员可以进行广泛选择。国际化体现在文档标准当中涉及到像语言的书写规范,有些文字是纵向的,或者是从右到左的书写系统,以及各个国家地区的历法、时间的处理。高保真迁移,文档的内容本身不需要开发者把所有的数据结构都实现出来,也能在呈现上表现一致。因为它的数据结构是非常庞大的,也提供了向后扩展的空间,业务扩展空间体现在向后兼容的设计,在文档当中可以嵌入多媒体的文件,比如 VBA 脚本、图片、音视频等。

TC45 是 ECMA 下属的技术委员会,主要负责对该标准的修订和维护。这个标准目前已经发展到第五版,一共有五部分。委员会的代表中有很多大家熟悉的公司,其中包括计算机厂商、互联网公司和传统行业代表,例如光学镜片公司、石油公司和图书馆等,可以发现图书馆在信息化的存储方面也有很多的需求。

右边两张图片是标准文档的下载页面截图,第一张图片是该标准第一部分在 ISO 上的页面,可以看到第一部分的内容有超过 5000 页的文档,数量还是比较多的。第二张图片是 ECMA 网站上该标准的截图,上面有对标准中 5 个部分的描述,我们可以访问网站下载标准文档进行查看。大家比较熟悉的像 Microsoft Office Excel、Apple Numbers、LibreOffice、Google Docs、Apache OpenOffice 等,都是遵循该标准的典型代表。

标准规范

下面介绍一下标准的内容,首先上层是标记语言部分,这部分由四类标记语言组成。

Word 文档对应的标记语言叫 WordprocessingML,电子表格则是 SpreadsheetML,PowerPoint 演示文稿对应的是 PresentationML。Excelize 主要实现的是 SpreadsheetML 这个部分,除此之外 Office 文档支持进行跨应用的嵌套,例如:Word 可以嵌套 Excel,Excel 可以嵌套 Word。通用标记是跨应用的文本标记语言,涉及到可视化图表、可扩展标记、源数据和目录引用等。

中间这一层叫做开放包装公约(Open Packaging Convention),简称 OPC,是组织这些文档格式的上层描述语言,其中定义了文档内部组件之间的关联关系、文档内部数据结构之间的依赖和文件数字签名等。

下面是底层的核心技术,我们可以创建一个 Excel 文件,修改成 zip 扩展名解压一下就知道它是个压缩包,标准委员会采用比较基础的核心技术来定义文档格式,这样做有很强的扩展性,也方便各个平台去实现。

XSD (XML Schema Definition)

为了方便大家理解后面的内容,先介绍一个技术名词 —— XSD。XSD(XML Schema Definition) 是 W3C(万维网联盟)推出的一个标准,用于定义 XML 的格式,XML 格式里面可以有无限的标签和属性,XSD 是用于描述 XML 的标记语言。

如上图所示的 XML 片段中有一个名为 employee 的标签,并包含两个子标签。它对应的 XSD 描述如下:

XSD 中的 element 标签与 XML 中的元素相对应,通过 name 属性定义标签名,type 属性定义其数据类型。上面例子中 firstname 和 lastname 两个子标签是 string 类型。

XML Schema Dependency

在文档格式标准当中,对数据结构的定义是通过 XSD 实现的,知道数据结构定义的方式之后,我们来梳理一下在 Excel 文档当中所涉及到的数据结构都有哪些。先看左侧的树状结构,这是一个 XSD 文件中所定义的 XML 标签示意图,XSD 之间可以互相引用。对 Excel 文档所涉及的主要数据结构定义 XSD 文件进行分析和梳理,绘制出一个依赖关系图就像右边这样,最上面是 sml.xsd,它是 Excel 文档的数据结构定义所使用的主文件,sml.xsd 中对通用标记语言相关的数据结构做了引用,其中包括依赖关系处理和简单数据类型的相关的结构定义。中间比较多的以 dml- 开头的 XSD 文件是与跨应用可视化相关的数据结构定义文件,例如图片、图表、图形和 SmartArt 等相关的数据结构都是在以 dml- 开头的 XSD 文件中定义。

大家看到 dml- 开头的 XSD 文件比较多,因为它是跨应用的,里面所涉及的 XML 标签和属性大概 8000 个左右,而 sml.xsd 中定义的标签和属性也有数千个之多。

格式解读

每个 Excel 文档都是一个工作簿,也叫做 Workbook,像这样一个简单的 Excel 文档,Workbook 中包含两个工作表(Worksheet),在名为 Sheet2 的工作表中有一些文本类型的数据,在名为 Sheet1 的工作表中创建两个图表,对 Sheet2 中的源数据进行引用。对于这样的一个简单 Excel 文档,我们把它像刚才讲到的那种方式进行分析,先把它的扩展名改一下然后解压看看,它的目录结构是这样子。通过目录结构中的文件名称,我也可以看出文档内部的数据结构是如何组织的:两个工作表对应 worksheets 文件夹中的 sheet1.xml 和 sheet2.xml,对图表和颜色、属性的定义都会从目录结构上体现。

典型关系图

这些文件的依赖关系如何?这边有一个图来解释,工作簿包含多个工作表,工作表包含图表、表格、透视表,透视表又包含透视缓存和透视记录。另外,工作簿还与主题、样式、公式以及字典等部分有关联关系。我们要去实现这个标准,也要把这些关系进行梳理。

XML 结构与依赖关系处理

如何去处理一个最简单的单元格?

上图左上角是一个简单的单元格,这个单元格的坐标是 D2,值是 Q1,带有蓝色背景色和黑色的实线边框样式。下面是读取或创建这样一个单元格的基本过程:图中的这些 XML 的片段来源于目录中的不同文件,每个 c 标签代表一个单元格,s 属性值为 7 代表单元格应用了索引为 7 的样式,我们去 styles.xml 当中找到索引为 7 的样式,通过 xf 标签的 fontId 和 fillId 属性可以确定字体 ID 和 单元格填充样式 ID,borderId 和 applyBorder 属性对应边框样式的定义和边框是否开启,numFmtId 是数字格式相关的定义。根据每一个字段向下去找,可以看到字号是 11,字体是 Calibri,包括单元格填充的颜色,颜色如果用到主题色,再去主题索引查找所对应的主题,找到颜色的色值,根据具体的色值表示方法,需要对色值进行换算。后面是对于边框的处理,根据边框 ID 为 2 找到它的上下左右边框样式定义。最后样式处理完成,到这里我们还没有看到对单元格值 Q1 的定义,这个图里没有体现出 Q1 的实际存储结构。大家可以看到 v 标签的值是 0,而不是 Q1 这个字符,由于该单元格的值是字符串类型,不会随单元格存储,根据索引 0  到字典中查找就可以读到单元格的值了。上面就是一个最基本的读取单元格的过程。

上述过程涉及到对多个文件的处理,这些文件有两种依赖关系:隐式依赖和显式依赖。举例来说,如果一个单元格有一个图片,图片不随着工作表一起存储,而是放在 media 文件夹当中,工作表并没有直接去指向某一个图片的地址,而是通过 ID 去根据 .rels 查找,这就构成一种隐式依赖。如果一个单元格有一个超链接,这个超链接也不随工作表存在一起,而是定义在工作表所对应的 relationship 中,在工作表中会显示声明超链接在 relationship 中的 ID,这是一种显式依赖。

02

Go 语言实现

至此,我们对国际电子表格文档格式标准已经做了一个整体的介绍,对于文档基本结构的构成和解析方法也有了初步的认识。下面将对关于使用 Go 语言对该标准实现过程中的一些经验做分享。

Excelize 技术架构

可以将 Excelize 技术能力划分为基础能力、样式处理能力、数据处理能力、图片/图表、工作簿/工作表、单元格和模型处理,7 大部分。

  • 基础能力 - 文件格式识别、媒体格式支持、元数据解析校验、OPC 封装与解构、依赖关系处理、扩展标记处理;

  • 样式处理能力 - 边框样式、冻结窗格、字体样式、行高 / 列宽、数字格式、色值计算;

  • 模型处理 - 模型组件化、模型校验、计算引擎、升级扩展能力、模型纠错验证;

  • 图片 / 图表 - 2D / 3D 处理、簇状 / 堆积 / 面积图、柱形 / 锥形 / 棱锥 / 饼图、气泡 / 散点 / 折线图、属性设置能力;

  • 工作簿 / 工作表 - 可见性设置、行 / 列处理、属性设置、工作表属性、页眉页脚、视图属性、搜索能力、数据保护、页面布局;

  • 单元格 - 数据类型支持、字典、选区合并、富文本、超链接、批注处理、公式处理、样式索引、单元格样式;

  • 数据处理能力 - 数据验证、时间处理、Crypto 加解密、单位转换、表格 / 过滤器、数据透视表、条件格式、VBA 脚本

对于模型的处理,后面会有代码介绍,其中涉及打开文件后,哪些部分需要加载到内存中做处理。大家可能有用过其他一些开源电子表格基础库,操作稍微复杂点的表格,很多情况下保存文件后用 Office 就打不开了,避免这类问题需要在兼容性方面做很多的处理。

图表可视化方面,Excelize 支持 52 种图表的生成,支持创建 2D 和 3D 图表,像柱状图、面积图、堆积图在实现上可以按照同一个类型的图表做处理,根据不同参数的变换来实现,还有柱形图、椎形图、饼图处理方式是相似的。属性设置能力包括了对图表坐标轴系列的绘制、网格线、透明度等属性的设置。

以上是目前 Excelize 的技术架构,后面会举三个例子介绍一下内部的具体实现。

文档结构定义

Excelize 中的 File 结构体⽤来定义⽂档模型,里面包括可导出和不可导出的字段。可导出字段包括工作表、样式、数据结构之间的引用关系等,开发者可以从外部访问到它们。在使用 Excelize 打开电子表格文档后,Excelize 会通过不可导出字段维护内部数据结构之间的依赖关系,在对电子表格进行读取或编辑的过程中,Excelize 会在运行时进行动态内存调节和纠错。

根据按需处理的设计原则,在外部 API 调⽤时,精确识别要操作哪些数据结构,按需加载所需处理的⽂档数据结构到对应的模型上,再通过内部处理器进⾏处理。这种处理⽅式在保证兼容性的同时,也提⾼了⽂档处理的性能。

Excelize 使用

1.创建电子表格文档

我在设计这个库的时候,使用方式上,也考虑到一些其他语言的开发者,希望降低开发者的学习成本,通过简单易用的函数可以实现快速接入。上面是一个创建电子表格文档的简单例子。使⽤ NewFile 新建 Excel ⼯作薄,新创建的⼯作簿中会默认包含⼀个名为 Sheet1 的⼯作表。如果需要生成比较大量的数据,Excelize 也提供了相关迭代的方法,支持在迭代循环过程中转换成相应的坐标。

2.图片图表处理

关于图片图表相关的处理,在处理图片图表的时候要考虑到兼容性,这是比较难实现的功能。在 Excelize 上添加图片和图表比较方便,基于刚才打开的文件调用 AddPicture,把图片路径传进去就可以了。如果有对图片缩放、打印效果等属性的设置,在函数的最后一个参数中声明即可,使用起来十分方便。此外还提供一个从电子表格文档中提取图片的 GetPicture 方法。

在电子表格文档中插入图片是怎么样实现的呢?对图片和图表的操作都涉及到标准中的 DrawingML 通用标记语言。在插入图片的时候,首先计算 DrawingID 是多少,接着对 relationship 进行更新,然后进行图片的数据结构调整;有些情况下图片还带有超链接,点击图片链接到 URL 或者电子表格内的某一个单元格,这里涉及到对依赖属性的处理;最后更新文件属性中对媒体格式的定义。对于图表来说更复杂一些,涉及到 Chart 模块。

上图是 Excelize 处理图片图表的逻辑时序图。在 Excelize 的  Drawing 子系统中,当开发者调用 AddChart 函数时先判断是否是首次在电子表格中添加图片或图表,再计算工作表与图片或图表之间的引用关系,得出对应的 Relationship ID 返回给内容定义部分,进行图表属性的处理,然后更新 drawing 对应的 Relationship,最后生成图表本身的数据结构,如果中间哪一步出现异常导致最后没有生成 Chart 数据,可以提前把对 Related Part 的处理过程终止,也不会影响最终生成的电子表格文档在被应用程序打开时的兼容性。

数据透视表

数据透视表在数据多维度分析领域中的很多业务场景都有使用,上图的例子中包括五列数据,有年份、月份、商品类别、商品销售额和商品销售区域,如果需要按照商品的分类、对月销售额按区域的汇总。对于这样多维度的数据分析,使用传统的条件过滤一次筛选是做不到的,而通过数据透视表可以方便的解决这个问题。

使用 Excelize 创建一个数据透视表也很简单,目前通过编程方式生成数据透视表的开源方法并不是很多。使用 Excelize 只需要几行代码就可以创建对应的数据透视表。根据对引用数据选区、透视表选区、筛选行和列(分别对应右侧四个象限中的筛选和排序字段)的定义,就可以生成这样的透视表。

在实现透视表功能的过程中,首先需要根据开发者定义的数据选区对数据进行计算,先生成数据透视缓存,基于数据透视缓存和数据筛选字段来生成数据透视表。

内存模型

关于内存模型的调节,大家试想一个场景,用户打开一个有 1 万行数据的 Excel 文档,打开之后有一个操作要把第 10 行删掉,那么从 11 行开始后面所有的数据,所包含的合并单元格、带有超链接的图片等所有与坐标相关的数据都会受影响,需要重新计算向上平移一行,在内存中要做实时调节,Excelize 内部有一个方法叫 adjustHelper,提供了四个参数:工作表名称,调节方向,调节基准和偏移量。调节方向包括横向和纵向,对应插入或删除行和列的情况,调节基准用以处理连续插入或删除行和列的情况。与坐标有关的电子表格数据结构:公式引用选区、数据透视表、超链接、批注、评论、分页、数据校验等,都需要做一系列的内存调节。

Go 处理多命名空间 XML 时的问题

下面分享一个在 Go 语言实现过程中遇到的问题,在实现国际电子表格文档格式标准过程中需要处理大量的 XML 的数据结构,其中涉及到很多带有命名空间的 XML。这样一段 XML:

entity 标签的子标签 a:val 有一个命名空间 a,通常情况在 Go 语言中通过定义 Entity 结构体来解析这段 XML,通过 encoding/xml 官方库进行反序列化,可以拿到 XML 原始信息,但是如果把这个结构体序列化回去,就会发现命名空间信息已经从 entity 标签转移到了子标签 val 上,而且子标签的命名空间前缀已经被丢掉了。

从 2015 年在 GitHub 上就有对这个问题的讨论,Go 语言作者之一 rsc 也对这些 issue 做了回应,不过目前还没有解决这些问题。

为了解决这个问题,定义两个结构体,在反序列化时的结构体上增加对命名空间属性的解析,在序列化时所对应的结构体上,利用 XML Tag 将命名空间前缀与属性名称通过冒号连接作为一个普通属性处理。

对于上面的例子,反序列化时将序列化得到的 XMLNSA 赋值给序列化结构体的 XMLNSA 上,通过这样的方式,可以保持序列化后的 XML 和原始的 XML 完全一致。

实现国际电子表格文档格式过程中,处理多命名空间的情况非常多。比如说 Excel 中的的迷你图(Sparkline)功能:迷你图是放入单元格中的小型图,可以展示所选内容中的一行数据的趋势。

这个功能在上面的示例文件中涉及到两个命名空间,第一个是名为 x14 的命名空间,对应的是 SparklineGroups 标签,代表在 Office 2010 版本后被支持的功能,第二个是名为 xm 的命名空间,做为 x14:SparklineGroups 的子标签,对应的是 sparkline 标签。

处理这样多命名空间的方式和刚才上面讲到的例子是一样,通过结构体的交换去补齐命名空间丢失的问题。

性能

目前 Excelize 写入 64 万个单元格要 2 秒钟左右,结合 Go 语言的并发优势,后续还有很大的优化空间。另外一个比较极端的场景测试:创建 1000 张图表耗时 10 秒。右图是 Excelize 与其他语言相关实现在生成 64 万个文本单元格下的性能表现情况,上方橙色条形图是耗时,下方蓝色条形图是内存使用情况。从上至下分别是基于 Go 语言实现的 Excelize、基于 Python 实现的 xlsxWriter、基于 Java 语言实现的 Apache POI、PHP 的 PhpSpreadsheet、还有基于 Node.JS 实现 SheetJS。综合来看 Go 语言的表现还是不错的,这是一个生成纯文本单元格的 Benchmark,Excelize 在开发之初更侧重与复杂文档格式的处理,在高级功能和兼容性上有更具优势。

03

总结

总结今天的话题,首先向大家介绍了国际电子表格文档格式标准,以及相关背景、特点和内容;对使用 Go 语言实现该标准相关的实践做了分享,其中涉及到了一系列核心功能的实现。Excelize 的开源也是对 Go 语言生态的一份贡献,希望帮助到更多有需要的朋友。Excelize 未来的发展规划包括了公式计算引擎、透视图、VBA 脚本以及更复杂的图表操作支持和性能的不断优化,同时也欢迎各位 Gopher 通过提交 Issue、PR、Donate 等各种形式参与到开源生态的建设中。

Q & A

Q:对性能后期优化和并发的思路?

A:在大规模数据搜索场景下,可以利用 Go 语言的优势通过多个协程进行分片搜索工作表内容,提升搜索速度,另外 Excelize 也支持对 XML 的流式读取,这样可以节省读取大文件时的内存

重磅活动预告

Gopher Meetup 上海站即将开启。来自蚂蚁金服&携程、趣头条、讯联数据、TutorABC的大咖讲师将带来 Go 开发领域的一线实践经验分享,尽在12月14日,微软加速器!

报名请戳:阅读原文

Go中国

扫码关注

国内最具规模和生命力的 Go 开发者社区

欢迎投稿,请联系:

situzhihui@163.com

聪明又努力的 Gopher 们,你“在看”我吗?

Go语言国际电子表格文档格式标准实践相关推荐

  1. 论通用病历文档格式标准

    论通用病历文档格式标准 南京都昌信息科技有限公司 袁永福 2016-11-21 本文首发于 http://www.hit180.com/23732.html 作者简介: 袁永福,男,南京东南大学毕业, ...

  2. 网站项目标准文档格式模版---网站项目建设流程概述(转)

     网站项目标准文档格式模版---网站项目建设流程概述(转) 一.概念 网站项目管理就是根据特定的规范.在预算范围内.按时完成的网站开发任务. 二.需求分析 项目立项 我们接到客户的业务咨询,经过双 ...

  3. 软件需求文档格式的标准写法

    软件需求文档格式的标准写法 1.引言 1.1    编写目的 • 阐明开发本软件的目的: 1.2    项目背景 • 标识待开发软件产品的名称.代码: • 列出本项目的任务提出者.项目负责人.系统分析 ...

  4. Office 文档解析 文档格式和协议

    本文讨论的 Office 文档指的是 Office 2007 及以后的 PPTX 和 xlsx 等格式的文件.在 Office 2007 之前使用的不公开标准的二进制格式定义.在 Office 200 ...

  5. 如何利用 onlyoffice 实现文档格式转换

    目录 前言 正文 启动 onlyoffice 服务 API 接口介绍 转换列表 请求示例 结尾 前言 日常生活和工作中,文档格式转换应该是很常见的需求.面对这样的需求,我们技术男有没有属于自己的好方法 ...

  6. Office Open XML 文档格式

    Office Open XML文档格式的详细说明以及规格是在2006年欧洲计算机制造商协会批准的一项标准, 编号是Ecma376, 到发稿时为止已经通过了ISO国际标准化组织的评审成为了一项国际标准I ...

  7. office文档格式简要介绍

    本文目的:理清office文档格式,学习何为OLE. 一.OpenXML (OOXML) OpenXML(OOXML) 是微软在Office 2007中提出的一种新的文档格式,Office 2007中 ...

  8. javadrawstring设置字符大小_LaTex学术写作——编辑文档格式 设置论文标题与摘要...

    在上一篇文章中,我们介绍了如何安装LaTex以及开始创建文档的命令.今天我们就从学术写作的第一步开始讲起:如何编辑论文首页标题以及论文摘要.最近正在做论文重现的作业,我选取的论文是David Card ...

  9. 将PDF和Gutenberg文档格式转换为文本:生产中的自然语言处理

    Estimates state that 70%–85% of the world's data is text (unstructured data). Most of the English an ...

最新文章

  1. 2022-2028年中国国学培训行业市场研究及前瞻分析报告
  2. 皮一皮:充实的一天...
  3. 汤家凤高等数学基础手写笔记-重积分
  4. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建
  5. ImmuniWeb 推出暗网数据泄露免费查询工具,使用超简单
  6. ASCII码表 0~127
  7. 解决虚拟机上的tomcat无法被主机访问的问题
  8. Swift的函数嵌套和返回内部函数
  9. promise 应用实例
  10. 使用MusicTools下载全网音乐
  11. 绿盟科技2019校园招聘笔试题附加题
  12. stimulsoft oracle,Stimulsoft Reports
  13. 自己搜的算法题2.0
  14. windows创建文件硬连接
  15. 对于“条件竞争”的利用
  16. 【Sa-Token】8、Sa-Token开启全局侦听器
  17. 获取星期PHP,php获取星期方法小结
  18. hue源码编译,替换cloudera manage hue,解决hue滚动条bug问题
  19. CUMT2021一道SSRF
  20. 成功解决keil识别不到单片机芯片,下载不了程序

热门文章

  1. linux登出用户,Linux系统中用户的登入登出命令详解
  2. vr直播是如何实现的?vr直播都有哪些优势
  3. window7安装过后有多大?6.18G
  4. 六大接口管理平台,总有一款适合你的!
  5. 鸿蒙系统卡顿解决方法,鸿蒙比安卓性能提升60%,还解决了安卓卡顿的问题,谷歌压力山大...
  6. 简单并不粗暴:自学产品的8个方法
  7. 【UVM基础】工厂(factory)机制快速上手指南
  8. linux系统fasta程序,快速计算fasta序列长度的方法
  9. 如何将 Django 服务器单独部署到 heroku 上
  10. 上计算机课睡觉检讨书400,上自习课睡觉检讨书500字范文【三篇】