如何读写ODF/ODS文件 (OpenDocument电子表格)
本文将介绍ODF格式的基础知识,特别是其在电子表格应用程序(OpenOffice.org Calc和Microsoft Office Excel 2007 SP2)中的实现。文章展示了一个将表格数据写入/读取到.ods文件的演示应用程序。
引言
OpenDocument格式(ODF)是一种基于XML的文件格式,用于表示电子文档,如电子表格、图表、演示文稿和文字处理文档。该标准由OASIS(结构化信息标准促进组织)开发,是一种**免费**且**开放**的格式。
ODF格式在免费软件和专有软件中都有使用。最初,该格式由**OpenOffice.org**办公套件实现,并且通过**Office 2007 SP2**,Microsoft也支持ODF的子集。
本文将介绍ODF格式的基础知识,特别是其在电子表格应用程序(**OpenOffice.org Calc**和**Microsoft Office Excel 2007 SP2**)中的实现。文章展示了一个将表格数据写入/读取到*.ods文件的演示应用程序。该应用程序使用Visual Studio 2010,用C#编写。创建的*.ods文件可以使用Excel 2007 SP2或更高版本以及OpenOffice.org Calc打开。
ODF格式
OpenDocument格式支持文档表示
- 作为单个XML文档
- 作为包内多个子文档的集合
办公应用程序使用第二种方法,我们将详细解释。
每个ODF文件是包(ZIP文件)内多个子文档的集合,每个子文档存储了完整文档的一部分。每个子文档存储文档的特定方面。例如,一个子文档包含样式信息,另一个子文档包含文档内容。
这种方法有以下好处
- 您无需处理整个文件即可提取特定数据。
- 图像和多媒体现在以原生格式编码,而不是文本流。
- 由于压缩和原生多媒体存储,文件体积更小。
包内有四个包含文件数据的子文档
- content.xml - 文档内容和内容中使用的自动样式
- styles.xml - 文档内容使用的样式以及样式本身使用的自动样式
- meta.xml - 文档元信息,例如作者或最后保存操作的时间
- settings.xml - 应用程序特定设置,例如窗口大小或打印机信息
除此之外,包内**可能**还有许多其他子文档,如文档缩略图、图像等。
要读取ODF文件中的数据,您需要
- 将包打开为ZIP存档
- 找到包含您要读取的数据的部分
- 读取您感兴趣的部分
另一方面,如果您想创建一个新的ODF文件,您需要
- 创建/获取所有必要的部分
- 将所有内容打包成具有适当扩展名的ZIP文件
电子表格文档
电子表格文档文件是ODF文件的子集。电子表格文件具有*.ods*文件扩展名。
内容(工作表)存储在content.xml子文档中。
正如我们在图 1 中看到的,工作表存储为XML元素。它们包含列和行定义,行包含单元格,依此类推……图中显示的是一个特定文档的数据,但由此我们可以看到content.xml文件的基本结构(您也可以下载完整的ODF规范)。
实现
我们的演示应用程序是使用Visual Studio 2010编写的C# WPF应用程序(图 2)。
该应用程序可以
- 创建一个新的电子表格文档。
- 读取现有的电子表格文档。
- 写入创建的电子表格文档。
创建新文档和应用程序的底层模型
在内部,电子表格文档存储为DataSet
。每个工作表由DataTable
表示,工作表的行由DataRow
表示,工作表的列由DataColumn
表示。因此,要创建一个新文档,我们必须创建一个新的DataSet
,其中包含DataTables
。每个DataTable
都有满足我们需求的行数和列数。
为了显示我们DataSet
中的数据(并允许编辑这些数据),应用程序动态创建带有DataGridViews
的选项卡(这些选项卡连接到我们的DataTables
)。
通过界面,用户可以读取、写入、编辑数据并向电子表格文档添加新行。
由于应用程序基本上将电子表格文档转换为/从DataSet 转换,因此它也可以用作Excel到DataSet导出/导入场景的参考。
Zip组件和XML解析器
虽然System.IO.Packaging
命名空间(.NET 3.0)中的类提供了一种读取和写入ZIP文件的方法,但它们需要不同格式的ZIP文件。因此,我们的演示使用了名为DotNetZip的开源组件。
使用ZIP组件,我们可以提取文件,获取子文档,替换(或添加)我们想要的子文档,并将该文件保存为.ods
文件(这是一个ZIP文件)。
对于文档处理,我们使用了XmlDocument
,因为它提供了一种轻松访问所需部分的方法。请注意,如果性能对您至关重要,您应该使用XmlTextReader
和XmlTextWriter
。该解决方案需要更多的工作(和代码),但提供了更好的性能。
读取电子表格文档
要读取文档,我们遵循以下步骤
- 提取*.ods*文件
- 获取content.xml文件(其中包含工作表数据)
- 从content.xml文件创建一个
XmlDocument
对象 - 创建一个
DataSet
(表示电子表格文件) - 使用
XmlDocument
,我们选择"table:table
"元素,然后创建相应的DataTables
- 我们解析"
table:table
"元素的子项,并将数据填充到DataTables
中 - 最后,我们返回
DataSet
并将其显示在应用程序的界面中
尽管ODF规范提供了一种指定默认行、列和单元格样式的方法,但实际实现存在一个糟糕的做法(尤其是在Excel中),它们宁愿将工作表写入为具有最大列数和最大行数的工作表,然后写入所有具有其样式的单元格。因此,您可能会看到您的工作表有超过1000列(Calc中有1024列,Excel中有16384列),甚至更多行(每行包含的单元格数量等于列数),尽管您只需要向前几行/列写入数据。
ODF规范提供了一种方法,您可以指定某个元素(如列/行/单元格),然后指定它重复的次数。因此,上述行为不会影响文件大小,但会使我们的实现变得复杂。
因此,我们不能仅仅读取列数并向DataTable
添加等量的DataColumns
(因为性能问题)。在此实现中,我们宁愿读取单元格,如果它们有数据,我们首先创建它们所属的行/列,然后将这些单元格添加到DataTable
中。因此,最终我们只分配了我们需要的空间。
写入电子表格文档
要写入文档,我们遵循以下步骤
- 提取template.ods文件(我们用作模板的*.ods*文件)
- 获取content.xml文件
- 从content.xml文件创建一个
XmlDocument
对象 - 从content.xml文件中删除所有"
table:table
"元素 - 从我们的
DataSet
读取数据并组合相应的"table:table
"元素 - 将"
table:table
"元素添加到content.xml文件中 - 将该文件压缩为新的*.ods*文件。
在此应用程序中,作为模板,我们必须使用一个空文档。但是,应用程序可以很容易地修改为使用其他模板(这样您就可以保留样式等)。
下载链接
您可以在此处下载演示应用程序的最新版本(包括C#源代码)。
历史
- 2009年7月24日:首次发布
- 2011年7月28日:项目从Visual Studio 2008 Windows Forms项目转换为Visual Studio 2010 WPF项目