软件随想录:程序员部落酋长Joel谈软件(local.joelonsoftware.com/wiki)-18

为什么微软 Office 的文件格式如此复杂以及一些解決方案)

From The Joel on Software Translation Project

Jump to: navigation, search

    上个礼拜,微软公开了他们 Office 软件的二进制文件格式。这些格式看起来真是太疯狂了。Excel 97-2003 的文件格式是一份 349 页的 PDF 档。等一下,这还不是全部喔!这份文件中有下面这句耐人寻味的注解:

    你看,Excel 97-2003 的文件其实是 OLE 复合文件;这意味著:基本上,每个文件中都存在著一份具体而微的文件系统。你得把另外的九份文件都读完才能全盘掌握,够复杂了吧且,这些「规格书」的內容看起来比较像是一堆 C 语言的数据结构,而不是我们凭经验所想像的那种规格书。它是一个完整的阶层式文件系统。

    如果你真的开始阅读这些文件,并且幻想著能利用周末的时间写些很炫的程序代码(像是把 Word 文档导入你的部落格中,或是把你的个人帐簿输出成 Excel 文件格式),这些又臭又长的规格书可能很快就会让你心灰意冷。面对这些 Office 文件格式,一个普通的程序员可能会有以下的结论:

  • 一定是故意搞得这么复杂,不想让人看懂的
  • 是一个神经错乱的柏格人搞出来的玩意儿
  • 当初是由一群疯狂的程序设计师制定的
  • 绝对不可能读取、建立这些文件而完全不出错

    以上四点全是错的。让我带你挖掘一些事实,告诉你为什么这些文件格式会复杂到令人难以想像,为什么它们不是「微软的程序写得很烂」的证明,以及你该如何面对这些事实的方法。

    首先我们必须要了解的是:这些二进制文件格式的设计目的和其他的文件格式(像是 HTML)完全不同。

    它们是为了能在老电脑上快速处理而设计的。对早期版本的 Windows 版 Excel 来说,内存的合理使用量是 1 MB,而且在 80386 20MHz 的电脑上应该要能跑得够顺。这些文件格式中有许多设计,是为了能更快速打开、储存文件而做的最佳化:

  • 这些文件是以二进制格式储存的,因此读取一笔记录通常只是单纯地「把一个范围的位元资料从磁碟片上搬(复制)到内存中」而已。所以最好的做法就是把文件格式订得跟 C 语言的数据结构一样,这样在读取文件的时候就完全不需要什么语法分析。通常使用语法分析所花的时间可能会比直接复制多上几个数量级。
  • 当有需要的时候,这些文件格式会使用一些非正常的手段,让常用的动作能快些。举例来说:Excel 95 和 Excel 97 有时会利用到一种叫做「简单储存」的功能,可以把文件快速地存成某种变种的 OLE 复合文件格式——因为原本的文件格式在实战中实在是不够快。Word 也有种东西叫做「快速储存」,在储存的时候只把有变动到的地方附加到文件末端,而不是重新写回整个文件。这么做可以让储存加快 14、15 倍。拿以前的硬盘来说,这意味著原本存一个大文件得花 13 秒,现在只要不到一秒就解決了。(这也意味著已经被刪除的资料还是会存在文件之中。最后这成了人们不想要的功能)

    这些设计是以既存的函式库为基础的。如果你打算从头开始写一套可以读取二进制格式的程序的话,你得要能支援 Windows Metafile Format(才能显示绘图图型)和 OLE 复合储存。如果你是在 Windows 下开发的话,这些功能都有函式库可以使用,你可以很轻松地完成这些功能…. 使用这些功能是微软团队的捷径。但如果你想要独力完成每一个功能的话,那你就得全部重写一次了。

    Office 大规模地支援复合文件格式。举例来说,你可以把试算表嵌到一份 Word 文件里面。一个合格的 Word 文件处理程序要能够很聪明地处理內嵌的试算表。

    它们不是为了你的脑袋瓜子设计的。这些格式设计的前提(以那个时间点来说是相当合理的)是:Word 的文件格式只需要能够被 Word 读取、写入,就够了。这意味著,每当 Word 开发团队的程序员要去決定怎么修改文件格式的时候,他只需要考虑的事情只有 (a)这样做够快吗(b)要怎么做才能在现有的 Word 程序代码中做最小幅度的修改就能达成目的。像是 SGML 和 HTML 那种追求「互换性、标准化」的文件格式,在那个 Internet 尚未兴起、文件互换性并不高的年代,并不是设计文件格式时的首要考量——Office 二进制文件格式的诞生时间可比他们早上十年。在那个时候的假设是:如果你需要做文件互换的话,你可以使用「导入/导出」功能。事实上 Word 也真的有一种为了能轻松达成互换的文件格式,称做 RTF。它从一开始就存在了,而且现在的 Word 还是 100% 支援这种格式。

    它们必须反应出应用软件的复杂度。每一个 checkbox、每一个格式选项,以及每一项微软 Office 中的功能,都必须反应在文件格式中的某个角落。那个在 Word 的「段落」选单中,让一个段落在必要的时候能移动到下一页、好让它能和下一段出现在同一页,叫做「与下段同页」的 checkbox得出现在文件格式中。而这也就是说,如果你想要实现一个能完美正确地读取 Word 文件的仿制品,你得要实现这个功能。如果你正在写一个足以和 Word 竞争、并且可以读取 Word 文件的文件处理程序,「从文件格式中读取这个设定」的程序代码也许只花你不到几分钟的时间,但你可能得要再花上几个礼拜的时间去调整你的页面规划演算法,好让结果看起来和 Word 一样。否则,你的雇客会用你的程序打开 Word 文件,然后发现所有的页面都乱七八糟。

    它们必须要能反应出应用程序的沿革。这些文件格式如此错综复杂,有很大一部份是为了反应那些老旧的、复杂的、讨人厌的,以及很少人用的功能。为了能向前兼容,它们依然存在于文件格式中——反正留著那些程序代码又不会增加微软什么负担。但如果你真的想要完整透彻地解译、写入这些文件格式,你得要重做一些 15 年前微软的实习生们做过的工作。我们得认清一件事实:最新版的 Word 和 Excel 是数千个开发人年的成果。如果你真的想要完完全全地复制这些应用程序的功能,你一样得花数千人年的工夫。文件格式只是简明扼要地列举了应用程序所支援的所有功能。

    来点有趣的吧!让我们深入一点看个小例子。Excel 的工作表是由一堆不同类型的 BIFF(译注:Binary Interchange File Format)记录组成的。我想要看规格书中的第一个 BIFF 记录的定义。它叫做 1904 记录。

    Excel 的文件格式规格对这个记录的说明很明显地含糊不清。它只提到 1904 记录代表了「是否使用了 1904 日期系统」。唉,又是一个典型的无用规格说明。要是你正在做 Excel 文件相关的程序开发工作,然后你在规格书中发现这个东西,你可能会断定微软又藏了一手。这短短的说明文字没办法给你足够的资讯。你还需要一些额外的知识,我就在这儿说明一下吧。Execl 的工作表可以分为两种:其中一种的日期计算是以 1900 年 1 月 1 日为纪元(包括一个为了和 1-2-3 兼容而存在的闰年错误,不过现在去谈它没啥意思),另一种则是以 1904 年 1 月 1 日为纪元。Excel 两种日期格式都支援,因为第一版的 Excel(Mac 版)直接使用操作系统的纪元系统(这样比较简单);但是 Windows 版的 Excel 必须要能导入使用 1900 纪元系统的 1-2-3 文件。这就足够让你欲哭无泪了。无论是过去还是现在,没有程序员不想依正道而行的;但有时候,你就是得屈服于现实。

    不管是1900还是1904 的文件都很常见,通常是端看那个文件是在 Windows 还是 Mac 上产生出来的。如果我们在使用者不知情的情况下自动转换格式,那么资料很可能会被损毀。因此,Excel 不会自动帮你转换。这不只是个把一个bit 从文件中读取出来的问题。这意味著你得把你的日期显示、处理程序代码整个翻修一遍,好同时支援这两种纪元系统。我想,这大概得花掉你好几天的时间去实现吧。

    没错,当你在写Excel 仿制品的时候,你会发现各式各样处理日期的微妙细节。Excel 在什么情况下会把数字转换成日期种格式化是怎么做的什么 1/31 会被解译成「1 月 31 日」,而 1/50 又会被解译成「1950 年 1 月 1 日」些微妙的动作没办法在文件上三言两语讲清楚;真的要详细地写成文件的话,那文件的內容大概就跟 Excel 的源代码没两样了。

    别忘了这只是几百个 BIFF 记录中的第一个而已,而且还是最简单的一个。大部份的记录都是复杂到会让合格的程序员崩溃的程度。

    唯一可能的结论是:微软释出他们的文件格式对大家是很有帮助的,但这并不代表导入或是储存 Office 文件就会变得比以前简单。这些应用软件的功能极为众多而复杂。你也没办法只实现出 20% 的功能,然后预期 80% 的使用者会满意。这些二进制文件的规格书,顶多只会帮你省下对这套复杂的系统做逆向工程的几分钟罢了。

    好吧!我答应要给你一些可行方案。好消息是:在绝大部份的一般情况下,想要去读些 Office 的二进制文件都是错误的选择。你可以认真地考虑采用另外的两种可行方案:让 Office 自己处理这些问题,或是使用比较容易读写的文件格式。

    让 Office 帮你处理这些繁复的工作。透过 COM Automation 机制,Word 和 Excel 提供了极为完整的对象模型。这个对象模型可以让你用写程序的方式完成任何事情。在许多情况下,你应该要重复利用 Office 內部的功能,而不是试著重新实现一次。以下是一些范例。

  1. 你有一个需要把现有的 Word 文件输出成 PDF 格式的 页介面应用程序。如果是我的话,我会这么做:写几行的 Word VBA 程序,读取一个 Word 文件,再利用 Word 2007 內建的 PDF 转出功能把文件存成 PDF 格式。你可以直接执行这几行程序——即使是从在 IIS 系统下执行的 ASP 或是 ASP.NET 程序中呼叫也没问题。它做得到的。第一次把 Word 唤起可能会花上几秒钟的时间。然后 COM 子系统会把 Word 留在内存中几分钟,以便你再需要它。所以,第二次执行会快得多。这样的速度对于一个 页介面应用程序来说是够快的了。
  2. 需求和前一点相同,但 页服务器得用 Linux。买一台 Windows 2003 服务器,安裝一套完全符合授权的 Word,然后写个小的 页服务程序做这件事。使用 C# 和 ASP.NET,大约是半天的工作量吧。
  3. 需求和前面相同,但规模非常大。丟一台负载平衡服务器在一堆你在第 2 点中建立起来的服务器前面。一行程序代码也不用写。

    这种方法适用于所有你可能服务器上处理的一般 Office 应用。举例来说:

  • 打开一份 Excel 工作簿,排序一些输入格中的资料,重新计算,然后把一些结果放在输出格中。
  • 使用 Excel 产生 GIF 格式的图表。
  • 不需花时间去思考文件格式,把各式各样的资料从任意种类的 Excel 工作表中捉出来。
  • 把 Excel 格式的文件转成 CSV 表格资料(另一种做法是透过 Excel ODBC 驱动程序,利用 SQL 查询把资料吸出来)。
  • 填写 Word 格式的表单。
  • 把文件在 Office 支援的各种格式间互转(你可以找到一大票文字处理程序和试算表文件的导入器)。

    在所有的这些情况里,你都有办法让 Office 对象在非互动模式执行,因此它们不会出现在屏幕上,也不会要求使用者输入任何东西。对了,如果你想用这个方法的话,要注意有一些容易发生问题的地方,而且微软对此并没有正式支援,所以在动手之前请先阅读他们提供的这篇 knowledge base 文件。

    使用比较简单写入的文件格式。如果你只是想要利用程序产生 Office 文件,你几乎一定能找到比 Office 二进制更好的格式可用,而且可以让 Word 和 Excel 顺利打开,没有任何问题。

  • 如果你只是想要产生给 Excel 使用的表格资料,请考虑使用 CSV。
  • 如果你真的需要 CSV 不支援的试算表计算功能,WK1 格式(Lotus 1-2-3 的文件格式)比 Excel 简单太多了,而且 Excel 也能无误地打开它。
  • 如果你真的、真的必须产生 Excel 原生文件,请试试古早版本的 Excel… Excel 3.0 是个不错的选择。没有什么复合文件的鬼东西,而且只储存你想用的最少功能。使用这样的文件格式,可以将你所需要输出的 BIFF 记录降到最低,然后你只要专心研究规格书的这些部份就好。
  • 至于 Word 文件,请考虑使用 HTML。Word 也能无误地打开。
  • 如果你真的希望在产生出来的 Word 文件里使用很炫的格式,你最好的赌注是产生 RTF 文件。Word 能做到的所有事情,都能用 RTF 表现出来。但 RTF 是以文字格式储存的,不是二进制格式,所以你可以在 RTF 文件中做些变动,然后 Word 依然读得出来。你可以用 Word 产生一份格式很漂亮的文件,在将来要修改的地方放些占位文字,存成 RTF 格式,然后再利用简单的文字替代,即时地把那些占位文字换成想要的文字。这样一来,你就有一份无论哪个版本的 Word 都可以顺利打开的 RTF 文件了。

    反正,除非你打算要写一套和 Office 竞争的软件,并且得要能完美地打开 Office 的文件,否则你大可把这几千个人年的工夫省下来。不管你打算解決什么样的问题,直接去读写 Office 的二进制文件是最浪费人力的一种方法。

Retrieved from ” http://local.joelonsoftware.com/wiki/%E7%82%BA%E4%BB%80%E9%BA%BC%E5%BE%AE%E8%BB%9F_Office_%E7%9A%84%E6%AA%94%E6%A1%88%E6%A0%BC%E5%BC%8F%E5%A6%82%E6%AD%A4%E8%A4%87%E9%9B%9C%EF%BC%9F%EF%BC%88%E4%BB%A5%E5%8F%8A%E4%B8%80%E4%BA%9B%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%A1%88%EF%BC%89″

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2013年1月20日
下一篇 2013年1月20日

相关推荐