Глава восемнадцатая. Почему форматы файлов Microsoft Office такие сложные (и как это обойти)
19 февpаля 2008 года, втоpник
На прошлой неделе Microsoft опубликовала форматы двоичных файлов Office. Выглядят они совершенно безумно. Описание форматов Excel 972003 — это PDF-документ из 349 страниц. Погодите, это еще не все! В документе есть такой интересный комментарий:
Каждая книга Excel хранится в составном файле.
Итак, файлы Excel 97-2003 — это составные (compound) OLE-документы , то есть, по сути, представляют собой файловую систему, заключенную в один файл. Это достаточно сложная система, и чтобы разобраться с ней, вам придется дополнительно прочесть 9-страничную спецификацию. Все эти «спецификации» больше похожи на структуры данных языка С, чем на спецификацию в привычном нам смысле. Это целая иерархическая файловая система.
Если вы взялись за эту документацию в надежде за выходные написать какой-нибудь хитрый код для импорта документов Word в свой блог или чтобы вывести данные личного финансового учета в виде таблицы Excel, сложность и объем спецификации быстро отобьют у вас это желание. Нормальный программист придет к выводу, что двоичные форматы Office:
умышленно запутаны,
выдуманы безумным киборгом,
созданы крайне неумелыми программистами,
корректно прочесть или записать их невозможно.
Во всех четырех случаях вы бы ошиблись. Проведем небольшое исследование, и я покажу вам, почему эти форматы так усложнились, почему они не свидетельствуют о непрофессионализме Microsoft и как все это обойти.
Форматы рассчитаны на очень старые компьютеры. В первых версиях Excel для Windows один мегабайт ОЗУ считался приемлемым объемом памяти, а двадцати мегагерц процессора 80386 было достаточно для комфортной работы Excel. Форматы файлов Microsoft существенно оптимизированы с целью ускорить открытие и сохранение файлов:
Это двоичные форматы, так что загрузка записи обычно сводится к копированию (blitting) некоторого сегмента байтов с диска в память, где они заполняют те самые структуры С, с которыми можно работать. Никакого лексического и синтаксического разбора, которые выполняются на порядок медленнее, чем копирование.
Формат задуман так, чтобы стандартные операции выполнялись быстро. Например, в Excel 95 и 97 есть режим Simple Save, применяемый как более быстрый вариант составного OLE-документа, если обычный документ оказывается недостаточно быстрым. В Word есть такая функция, как Fast Save: чтобы быстро сохранить длинный документ, сделанные изменения чаще всего дописываются в конец файла, чтобы не переписывать весь файл заново. На старых жестких дисках это означало, что большой документ записывался в течение одной секунды вместо тридцати. (Правда, при этом в файле сохранялись удаленные из документа данные. Не всем это понравилось.)
Форматы были рассчитаны на использование библиотек. Если вам нужно было написать собственную программу импорта, требовалось обеспечить поддержку таких вещей, как Windows Metafile Format (для графики) и составные OLE-документы. Если вы работаете в Windows, то все тривиально, поскольку соответствующие библиотеки там есть. Воспользовавшись ими, команда Microsoft облегчила себе задачу. Но если вы пишете что-то свое с чистого листа, вам придется реализовать все это самостоятельно.
Office хорошо поддерживает составные документы: например, в документ Word можно вставить таблицу Excel. В идеале программа импорта файла Word должна разумно обработать встроенную таблицу Excel.
Обмен файлами с другими приложениями не предполагался. В то время считалось, что файл в формате Word следует читать и записывать только с помощью программы Word. Из этого следовало, что разработчикам Word, если они хотели изменить формат, требовалось позаботиться лишь о том, чтобы: а) формат работал быстро; б) в базовый код Word нужно было внести минимум изменений. Такие идеи, как SGML и HTML — стандартизированные, пригодные для обмена данными форматы, — возникли лишь с появлением возможности обмена документами через Интернет; двоичные форматы Office разрабатывались за десятилетие до этого. Всегда предполагалось, что для обмена документами есть экспорт и импорт. На самом деле, в Word имеется формат RTF, разработанный для облегчения обмена документами, и он там есть практически с самого начала. RTF полностью поддерживается и сейчас.
Форматы должны отражать все сложные особенности приложений.
Каждый флажок в окнах диалога, каждое свойство форматирования, каждая функция Microsoft Office должны быть записаны где-то в файле. В окне Абзац есть флажок не отрывать от следующего, который задает перенос абзаца на новую страницу, если требуется, чтобы он был на одной странице со следующим абзацем. Состояние этого флажка должно быть учтено в формате. А это значит, что если вам нужно написать точный клон Word, способный корректно читать документы Word, вы должны реализовать и эту функцию. Создавая конкурентоспособный текстовый процессор, который должен загружать документы Word, вы за минуту напишете код, считывающий этот бит из файла. Но чтобы учесть этот флажок, вам придется переделать свой алгоритм создания макета страницы, на что могут уйти недели. Если вы не сделаете этого, то пользователи вашего клона, открыв в нем свои файлы Word, увидят искаженные страницы.
Форматы должны отражать историю развития приложений. Многие особенности формата связаны со старыми, сложными, противными и редко используемыми функциями. Они сохранены ради обратной совместимости, а еще потому, что Microsoft ничего не стоит оставить старый код. Но если вы действительно хотите тщательно обрабатывать эти файлы, вам придется заново повторить всю ту работу, которую 15 лет назад сделали в Microsoft какие-нибудь практиканты. Вывод из этого такой: в нынешние версии Word и Excel вложены уже тысячи человеко-лет труда, и если вы хотите их точно клонировать, вам придется работать тысячи лет. Формат файла — это лишь краткая сводка функций, поддерживаемых приложением.
Рассмотрим подробно один маленький пример. Файл Excel — это набор записей формата BIFF разных типов. Посмотрим, что сказано в спецификации про первую запись BIFF. Она называется 1904.
Спецификация Excel по поводу этой записи крайне немногословна: запись 1904 указывает на «использование системы дат 1904». Классический пример бесполезной спецификации. Когда программист, работающий с форматом Excel, обнаруживает в спецификации формата такой текст, он может заподозрить, что Microsoft здесь что-то скрывает. В этой информации недостаточно информации. Нужно знать еще что-то, о чем я вам сейчас и поведаю. Есть два вида таблиц Excel: с датами, отсчитываемыми от 1 января 1900 года (вместе с умышленной ошибкой, которая считает 1900 год високосным, сделанной для совместимости с Lotus 1-2-3, о чем здесь неинтересно рассказывать), и с датами, отсчитываемыми от 1 января 1904 года. Excel поддерживает обе системы, потому что первая версия писалась для Маков, где операционная система использовала отсчет дат от 1904 года, и так было проще, а Excel для Windows должна была импортировать файлы Lotus 1-2-3, где отсчет велся от 1 /1/1900. Тут есть от чего заплакать. Когда только программисты не ошибались, и вот вам пример.
Вы можете встретить файлы обоих типов — и 1900, и 1904, обычно в зависимости от того, где они были созданы — в Windows или на Маке. Преобразование из одного формата в другой может нарушить целостность данных, поэтому Excel не станет менять тип файла автоматически. Если вы хотите работать с файлами Excel, вам придется реализовать поддержку обоих типов. И тут загрузкой одного бита из файла не обойтись. Вам придется переписать весь код отображения дат и разбора, чтобы поддерживать оба формата отсчета дат. Думаю, это займет несколько дней.
Далее, если вы пишете клон Excel, то перед вами возникнет масса мелких и скрытых проблем обработки даты. Когда Excel конвертирует числа в даты? Как работает форматирование? Почему «1/31» интерпретируется как 31 января текущего года, а «1/50» — как 1 января 1950 года? Если описывать все эти тонкости поведения, придется написать документ, по объему информации сравнимый с исходными текстами Excel.
И это только первая из сотен записей BIFF, которые вам придется поддерживать, и одна из простейших. Многие из них настолько сложны, что доведут до слез и опытного программиста.
Из этого только один вывод. Очень мило, что Microsoft опубликовала формат файлов Office, но задача импорта или сохранения в форматах Office от этого не стала проще. Программы Office безумно сложны и богаты функциями, и нельзя рассчитывать, что, реализовав 20% наиболее популярных, вы сможете осчастливить 80% пользователей. Спецификация двоичных файлов, по сути, сохранит только несколько минут, потраченных на дизассемблирование чрезвычайно сложной системы.
Да, я обещал рассказать об обходных путях. Прежде всего, в большинстве популярных программ попытки читать и записывать данные в двоичных форматах Office — результат ошибочного решения. Есть две основные альтернативы, заслуживающие внимания: возложить эту обязанность на Office либо воспользоваться более простыми форматами файлов.
Заставить Office выполнять тяжелую работу. Word и Excel основаны на очень сложных объектных моделях, доступных посредством COM-автоматизации, что позволяет программно делать все. Во многих случаях проще использовать код Office, чем пытаться заново реализовать его. Вот несколько примеров.
1. У вас есть веб-приложение, которое должно выводить ваши файлы Word в формате PDF. Я бы это реализовал так: несколько строк кода VBA для Word загружают файл и сохраняют его путем экспорта в PDF, имеющегося в Word 2007. Этот код можно запустить напрямую, например из ASP или ASP.NET под IIS. И это будет работать. Первый раз Word будет запускаться несколько секунд. Дальше будет быстрее, потому что подсистема COM держит Word в памяти несколько минут на случай, если он потребуется снова. Все это достаточно быстро для веб-приложения.
2. То же самое, но хостинг на Linux. Купите один сервер под Windows 2003, установите на него лицензионный Word и создайте маленький веб-сервис, который сделает все, что нужно. Полдня работы на C# и ASP.NET.
3. То же самое, но требуется масштабировать решение. Возьмите столько машин, описанных на шаге 2, сколько нужно, и поставьте балансировщик нагрузки. Программирование не требуется.
Такой подход работает для большинства офисных задач, которые могут выполняться на вашем сервере. Например:
• Открыть книгу Excel, записать данные во входные ячейки, произвести пересчет и получить результаты в выходных ячейках.
• Использовать Excel для генерации диаграмм в формате GIF.
• Извлечь любую информацию из любой таблицы Excel, не ломая себе голову над форматами файлов.
• Конвертировать файл Excel в формат CSV (другой способ — использовать ODBC-драйверы для Excel и получить данные через SQL-запрос).
• Редактировать документы Word.
• Заполнять формы Word.
• Конвертировать данные между многочисленными форматами, поддерживаемыми Office (имеются средства импорта для десятков форматов текстовых процессоров и электронных таблиц).
Во всех этих случаях есть способы сообщить объектам Off^, что они работают не в интерактивном режиме и не должны перерисовывать экран, как и требовать ввода данных пользователем. Кстати, если вы хотите идти этим путем, есть несколько ловушек и нет официальной поддержки, так что сначала прочтите статью в базе знаний Microsoft (http://support.mi-crosoft.com/default.aspx?scid=25775 7).
Воспользоваться более простым форматом для записи файлов. Если вам нужно только программно создавать документы для Office, есть много других форматов, которые Office уверенно откроет, не пропустив ни байта.
Если вам нужно записать данные в таблицу для использования в Excel, попробуйте CSV.
Если нужны табличные расчеты, которые CSV не поддерживает, то есть формат формат WK1 (Lotus 1-2-3), который намного проще и отлично открывается в Excel.
Если совершенно необходимо создавать именно файлы Excel, найдите какую-нибудь древнюю версию, например 3.0, в которой нет всех этих составных документов, и сохраните минимальный файл, в котором есть только нужные вам функции. По этому файлу определите, какой минимум BIFF-записей вам потребуется выводить, и изучите только соответствующую часть спецификации.
Если нужно вывести документы для Word, можно использовать HTML. Word хорошо открывает файлы в этом формате.
Если требуется сложное форматирование документа для Word, ваш выбор — RTF. Все, что есть в Word, можно записать в RTF, но это текстовый, а не двоичный формат, поэтому можно изменить данные в RTF-документе, сохранив корректность файла. Например, создаете в Word красиво отформатированный документ с фиктивными данными в нужных полях и с помощью простой текстовой замены динамически заменяете их. Этот RTF-документ будет отлично открываться в любой версии Word.
Во всяком случае — если только вы действительно не собрались выпустить программу-конкурент Office, которая будет идеально считывать и записывать все документы Office, и тогда у вас впереди тысячи человеко-лет работы, — чтение и запись двоичных форматов Office наверняка окажется самым трудоемким этапом в решении вашей задачи.