HotXLS отваря работни книги, записани от Excel 2.0, 3.0 и 4.0, директно от Delphi и C++Builder. Тези файлове предшестват OLE контейнера за съставни документи, който използва всеки по-късен .xls, така че те са сурови потоци от BIFF записи без никаква обвивка за съхранение, а четец, изграден за BIFF8, няма да намери нито една разпознаваема структура вътре в тях. Отварянето на такъв файл използва същото извикване Open както при всяка друга работна книга; четецът открива формата и превключва пътищата
Тези файлове все още се появяват, което е единствената причина изобщо нещо от това да има значение. Инженерни архиви, задължително съхранение на държавни архиви, лабораторни данни от инструменти, чийто софтуер за управление е написан през 1993 г., и дългогодишни счетоводни системи всички са оставили след себе си работни книги BIFF2 и BIFF4. Съвременният Excel направо отказва да отвори няколко от тях, след като е премахнал наследени конвертори по причини, свързани със сигурността, което оставя набор от данни, който никой не може да прочете с инструмент, който някой има
Какво прави работна книга от преди OLE различна?
Всеки .xls от Excel 5.0 нататък е OLE2 съставен файл — малка файлова система вътре във файл, като работната книга живее в поток с име Workbook или Book. Анализирането на такъв файл започва с анализиране на този контейнер, както е описано в двоичния формат на съставен файл в Pascal
BIFF2 до BIFF4 нямат контейнер. Файлът започва веднага със запис BOF, а номерът на записа на този BOF кодира поколението: $0009 за BIFF2, $0209 за BIFF3 и $0409 за BIFF4. HotXLS валидира дължината на тялото на BOF, която е между четири и шест байта, и типа подпоток — $0010 за работен лист, $0020 за диаграма и $0040 за лист с макроси — преди да се ангажира с суровия път. Тази валидация е това, което пази повреден или неправилно идентифициран файл от интерпретиране като много стара работна книга
Три поколения, три оформления на записи
Записите за клетки са мястото, където поколенията се разминават най-видимо. BIFF2 заема непрекъснат блок от ниски номера на записи, $0001 до $0005 за празни, цели числа, числа, надписи и клетки с булеви или грешни стойности, а всяко тяло носи тристайно поле с атрибути, там, където по-късните версии поставят индекс на разширен формат. BIFF3 и BIFF4 изоставят това и повторно използват номерата и оформленията на записите на BIFF5, $0201, $0203, $0204 и $0205, с двубайтов XF индекс
Тази последна подробност причинява конкретен и лесно погрешно диагностициран провал. Запис LABEL на BIFF3 или BIFF4 е структурно идентичен на своя аналог на BIFF5 — ред и колона, следвани от индекса на формата и след това броя знаци. Напишете четец, който приема оформлението на BIFF2, и той чете два байта твърде малко, след което излиза извън края на записа и погрешно интерпретира всичко след него. Симптомът не е изключение; това е работна книга, която се чете с правдоподобни боклуци в нея
Записите за формули заемат паралелна номерация и в трите, $0006, $0206 и $0406. Когато формула произвежда резултат от тип низ, този низ пристига в отделен следващ запис, $0007 или $0207, а формата на BIFF2 на това използва еднобайтов префикс за дължина, вместо двубайтовия, използван по-късно
Защо формулите се връщат като стойности, а не като текст
HotXLS чете кеширания резултат от формула в тези файлове и не се опитва да пресъздаде израза на формулата. Това е съзнателна граница, а не пропуск, който чака да бъде запълнен
Анализираният израз в BIFF2 до BIFF4 използва кодиране на токени, което се различава от BIFF5 и по-късните по начини, надхвърлящи козметиката: дължините на токените са с различни префикси, токените за препратки имат различни размери, а таблиците с индекси на функции са преномерирани между поколенията. Прекарването на тези байтове през преводач за изрази от BIFF8 не произвежда грешна формула, то произвежда случайна. Четенето на кешираната стойност ви дава числото или низа, които Excel последно е изчислил, което е точно това, от което една архивна миграция реално се нуждае
Кешираната стойност живее на отместване, зависещо от поколението, вътре в записа: байт 7 за BIFF2 и байт 6 за BIFF3 и BIFF4. Специални стойности — низове, булеви стойности, грешки и празни — се кодират в маркерна дума $FFFF с дискриминатор, същата конвенция, която по-късните поколения на BIFF са запазили
Отваряне на файл
Извикващият код е нищо особено, и точно в това е смисълът. Разпознаването се случва вътре в Open:
uses
lxHandle;
var
Book: TXLSWorkbook;
Sheet: TXLSWorksheet;
R, C: Integer;
V: Variant;
begin
Book := TXLSWorkbook.Create;
try
if Book.Open('archive\1993-inventory.xls') <> 1 then
begin
Writeln('unreadable - quarantine for manual review');
Exit;
end;
Sheet := Book.Sheets[1]; // Sheets[] е базирано на 1
for R := Sheet.UsedRange.FirstRow + 1 to Sheet.UsedRange.LastRow + 1 do
for C := Sheet.UsedRange.FirstCol + 1 to Sheet.UsedRange.LastCol + 1 do
begin
V := Sheet.Cells[R, C].Value;
if not VarIsEmpty(V) then
Writeln(Format('R%dC%d = %s', [R, C, VarToStr(V)]));
end;
finally
Book.Free;
end;
end;
Обърнете внимание на аритметиката на индексите в този цикъл. Границите на UsedRange са базирани на 0, докато както колекцията от листове, така и достъпът до клетки са базирани на 1 — несъответствие, предшестващо текущото API и запазено заради съвместимост. Пропускането на корекцията одитира грешния правоъгълник и не отчита нищо необичайно, докато го прави. Евтини предварителни проверки, които избягват изобщо зареждане на файл, са разгледани в лека инспекция на работна книга
Какво не получавате и какво да направите по въпроса
Форматирането не се интерпретира. HotXLS не анализира записите XF и FONT на тези поколения, така че шрифтове, цветове, рамки и числови формати са недостъпни, а клетки, които Excel някога е показвал като дати, се връщат като своите сурови серийни числа
Последното изисква обработка във вашия собствен код, а не в четеца, и причината е честна: числовите формати в BIFF2 до BIFF4 не са достатъчно надеждни, за да задвижат автоматично решение за дата. Колона от петцифрени числа може да са дати, а може да са номера на части. Преобразувайте съзнателно, използвайки системата на дати на работната книга, чиито правила са описани в серийни номера на дати, системата 1904 и числови формати:
// Решавайте по колона, никога по стойност: петцифрено число може да е
// дата или номер на част, а наследеният формат няма да ви каже
if ColumnHoldsDates(C) then
begin
// Двете системи на дати са на 1462 дни разстояние, така че един и
// същ сериен номер означава две дати, отдалечени с четири години.
// Прочетете системата от работната книга, вместо да я предполагате
if Book.Date1904 then
Writeln(DateToStr(SerialToDate1904(V)))
else
Writeln(DateToStr(SerialToDate1900(V)));
end
else
Writeln(VarToStr(V));
Две структурни бележки допълват картината. Записите за защита с парола и кодова страница се появяват вътре в единствения поток на работен лист, вместо в поток на ниво работна книга, защото няма поток на ниво работна книга, в който да бъдат поставени, така че трябва да бъдат разпознати в контекста на работния лист. А файл от BIFF2 до BIFF4 съдържа точно един подпоток за лист; работни книги с няколко листа не са съществували, докато форматът не е получил своя контейнер
Прагматичният път за миграция следователно е двустъпков: прочетете наследения файл заради стойностите му, след което запишете съвременна работна книга, която носи тези стойности с форматиране, приложено от вас самите. Наследеното четене, съвременното записване и всичко между тях работят в една библиотека за Delphi и C++Builder, описана на страницата на HotXLS Delphi компонента за електронни таблици