A HotXLS közvetlenül Delphiből és C++Builderből nyitja meg az Excel 2.0, 3.0 és 4.0 által írt munkafüzeteket. Ezek a fájlok megelőzik az OLE összetett dokumentumkonténert, amelyet minden későbbi .xls használ, így nyers BIFF rekordfolyamok, egyáltalán tárolóburkolat nélkül, egy BIFF8-hoz épített olvasó pedig egyetlen felismerhető struktúrát sem talál bennük. Egy ilyen megnyitása ugyanazt az Open hívást használja, mint bármely más munkafüzet; az olvasó felismeri a formátumot, és útvonalat vált
Ezek a fájlok még mindig felbukkannak, és ez az egyetlen ok, amiért ez egyáltalán számít. Mérnöki archívumok, kormányzati iratmegőrzés, olyan műszerek laboratóriumi adatai, amelyek vezérlőszoftverét 1993-ban írták, és régóta futó könyvelési rendszerek egyaránt hagytak maguk után BIFF2 és BIFF4 munkafüzeteket. A modern Excel egyenesen elutasítja néhányuk megnyitását, mivel biztonsági okokból eltávolította a régi konvertereket, ami olyan adathalmazt hagy hátra, amelyet senki nem tud elolvasni olyan eszközzel, amilyen bárkinél van
Mitől más egy OLE előtti munkafüzet?
Minden .xls az Excel 5.0-tól kezdve egy OLE2 összetett fájl, egy kis fájlrendszer egy fájlon belül, ahol a munkafüzet egy Workbook vagy Book nevű streamben él. Az elemzés ennek a konténernek az elemzésével kezdődik, amelyet az összetett fájl bináris formátuma Pascalban ismertet
A BIFF2-től BIFF4-ig nincs konténer. A fájl azonnal egy BOF rekorddal kezdődik, és ennek a BOF-nak a rekordszáma kódolja a generációt: $0009 a BIFF2-nél, $0209 a BIFF3-nál és $0409 a BIFF4-nél. A HotXLS ellenőrzi a BOF-törzs hosszát, amely négy és hat bájt között van, valamint az alfolyam típusát, $0010 egy munkalapnál, $0020 egy diagramnál és $0040 egy makrólapnál, mielőtt elköteleződne a nyers útvonal mellett. Ez az ellenőrzés akadályozza meg, hogy egy sérült vagy tévesen azonosított fájlt egy nagyon régi munkafüzetként értelmezzen a rendszer
Három generáció, három rekordelrendezés
A cellarekordoknál térnek el a generációk a legláthatóbban. A BIFF2 a rekordszámok egy összefüggő, alacsony tartományát foglalja el, $0001-től $0005-ig üres, egész szám, szám, felirat és logikai-vagy-hiba cellákhoz, minden törzs pedig egy hárombájtos attribútummezőt hordoz ott, ahol a későbbi verziók egy kiterjesztett formátumindexet tesznek. A BIFF3 és a BIFF4 elhagyja ezt, és újrahasznosítja a BIFF5 rekordszámait és elrendezéseit, $0201, $0203, $0204 és $0205, egy kétbájtos XF-indexszel
Ez az utolsó részlet egy konkrét és könnyen félrediagnosztizálható hibát okoz. Egy BIFF3 vagy BIFF4 LABEL rekord strukturálisan azonos a BIFF5-beli megfelelőjével, sor és oszlop, majd a formátumindex, majd a karakterszám. Ha egy olvasót a BIFF2 elrendezését feltételezve írsz, az két bájttal kevesebbet olvas, majd túllép a rekord végén, és mindent félreértelmez, ami utána következik. A tünet nem egy kivétel; egy olyan munkafüzet, amely hihető szemétadatokkal olvasódik be
A képletrekordok mindhárom generációban párhuzamos számozást foglalnak el, $0006, $0206 és $0406. Amikor egy képlet stringeredményt ad, az a string egy külön, azt követő rekordban érkezik, $0007 vagy $0207, a BIFF2-beli formája pedig egybájtos hosszúságelőtagot használ a később alkalmazott kétbájtos helyett
Miért érkeznek vissza a képletek értékként, nem szövegként?
A HotXLS ezekben a fájlokban egy képlet gyorsítótárazott eredményét olvassa, és nem próbálja rekonstruálni a képletkifejezést. Ez egy szándékos határ, nem egy betöltésre váró hiányosság
A BIFF2-től BIFF4-ig elemzett kifejezés olyan tokenkódolást használ, amely kozmetikainál is jelentősebben tér el a BIFF5-től és a későbbiektől: a tokenhosszak eltérően vannak előtaggal ellátva, a hivatkozás-tokenek mérete eltérő, és a függvényindex-táblákat generációk között átszámozták. Ha ezeket a bájtokat egy BIFF8 kifejezésfordítón futtatjuk át, az nem egy rossz képletet állít elő, hanem egy véletlenszerűt. A gyorsítótárazott érték olvasása azt a számot vagy stringet adja, amelyet az Excel utoljára kiszámított, és pontosan erre van szüksége egy archívummigrációnak
A gyorsítótárazott érték a rekordon belül egy generációtól függő eltolásnál él: a 7. bájtnál BIFF2-nél, a 6. bájtnál BIFF3-nál és BIFF4-nél. A speciális értékek, stringek, logikai értékek, hibák és üres cellák egy $FFFF jelölőszóban vannak kódolva egy megkülönböztetővel, ugyanazzal a konvencióval, amelyet a későbbi BIFF-generációk is megtartottak
Egy fájl megnyitása
A hívó kód semmi különös, és pontosan ez a lényeg. A felismerés az Open-en belül történik:
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]; // A Sheets[] egytől indexelt
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;
Figyeld meg az indexaritmetikát abban a ciklusban. Az UsedRange határai nullától indexeltek, míg mind a munkalapgyűjtemény, mind a cellaelérés egytől indexelt, egy olyan következetlenség, amely megelőzi a jelenlegi API-t, és kompatibilitás miatt maradt meg. Az igazítás elfelejtése rossz téglalapot ellenőriz, és eközben semmi szokatlant nem jelent. Az olyan olcsó előellenőrzéseket, amelyek elkerülik egy fájl teljes betöltését, a könnyűsúlyú munkafüzet-vizsgálat ismerteti
Mit nem kapsz meg, és mit tegyél vele?
A formázást nem értelmezi a rendszer. A HotXLS nem elemzi ezeknek a generációknak az XF és FONT rekordjait, így a betűtípusok, színek, keretek és számformátumok nem elérhetők, azok a cellák pedig, amelyeket az Excel egykor dátumként jelenített meg, nyers sorszámukként érkeznek vissza
Ezt az utolsót a saját kódodban kell kezelned, nem az olvasóban, és az ok őszinte: a BIFF2-től BIFF4-ig terjedő számformátumok nem elég megbízhatók egy automatikus dátumdöntés vezérléséhez. Egy ötjegyű számokból álló oszlop lehet dátum, vagy lehet cikkszám. Konvertálj tudatosan, a munkafüzet dátumrendszerét használva, amelynek szabályait a dátum-sorszámok, az 1904-es rendszer és a számformátumok ismerteti:
// Oszloponként dönts, sosem értékenként: egy ötjegyű szám lehet
// dátum vagy cikkszám, a régi formátum pedig nem árulja el
if ColumnHoldsDates(C) then
begin
// A két dátumrendszer 1462 nap távolságra van egymástól, így ugyanaz a
// sorszám két, négy évvel eltérő dátumot jelöl. A rendszert a
// munkafüzetből olvasd ki, ne feltételezz egyet
if Book.Date1904 then
Writeln(DateToStr(SerialToDate1904(V)))
else
Writeln(DateToStr(SerialToDate1900(V)));
end
else
Writeln(VarToStr(V));
Két strukturális megjegyzés egészíti ki a képet. A jelszóvédelem és a kódlaprekordok az egyetlen munkalap-streamen belül jelennek meg, nem egy munkafüzet-szintű streamben, mert nincs olyan munkafüzet-szintű stream, amelybe elhelyezhetők lennének, így munkalap-kontextusban kell felismerni őket. Egy BIFF2-től BIFF4-ig terjedő fájl pedig pontosan egy munkalap-alfolyamot tartalmaz; a többmunkalapos munkafüzetek nem léteztek addig, amíg a formátum meg nem kapta a konténerét
A pragmatikus migrációs útvonal ezért kétlépéses: olvasd be a régi fájlt az értékeiért, majd írj egy modern munkafüzetet, amely ezeket az értékeket a saját magad által alkalmazott formázással hordozza. A régi fájlok olvasása, a modern írás, és minden, ami a kettő között van, egyetlen könyvtárban fut Delphihez és C++Builderhez, amelyet a HotXLS Delphi táblázatkezelő-komponens oldala ismertet