HotXLS åbner arbejdsbøger skrevet af Excel 2.0, 3.0 og 4.0 direkte fra Delphi og C++Builder. Disse filer går forud for den OLE-sammensatte dokumentcontainer, som hver senere .xls bruger, så de er rå BIFF-poststrømme uden nogen lagringswrapper overhovedet, og en læser bygget til BIFF8 finder ikke en eneste genkendelig struktur inde i dem. At åbne en bruger det samme Open-kald som enhver anden arbejdsbog; læseren registrerer formatet og skifter sti
Filerne dukker stadig op, hvilket er den eneste grund til, at noget af dette betyder noget. Tekniske arkiver, offentlige journalopbevaringskrav, laboratoriedata fra instrumenter, hvis kontrolsoftware blev skrevet i 1993, og langvarige regnskabssystemer har alle efterladt BIFF2- og BIFF4-arbejdsbøger. Moderne Excel nægter helt at åbne flere af dem, efter at have fjernet legacy-konvertere af sikkerhedsgrunde, hvilket efterlader et datasæt, ingen kan læse med et værktøj, nogen har
Hvad gør en pre-OLE-arbejdsbog anderledes?
Hver .xls fra Excel 5.0 og fremefter er en OLE2-sammensat fil, et lille filsystem inde i en fil, hvor arbejdsbogen bor i en strøm ved navn Workbook eller Book. At parse en begynder med at parse den container, som beskrevet i det sammensatte binære filformat i Pascal
BIFF2 til og med BIFF4 har ingen container. Filen starter øjeblikkeligt med en BOF-post, og postnummeret for den BOF koder generationen: $0009 for BIFF2, $0209 for BIFF3 og $0409 for BIFF4. HotXLS validerer BOF-krops-længden, som er mellem fire og seks bytes, og delstrømtypen, $0010 for et regneark, $0020 for et diagram og $0040 for et makroark, før den forpligter sig til den rå sti. Den validering er det, der forhindrer en korrupt eller fejlidentificeret fil i at blive fortolket som en meget gammel arbejdsbog
Tre generationer, tre postlayouts
Celleposterne er der, hvor generationerne afviger mest synligt. BIFF2 optager en sammenhængende blok af lave postnumre, $0001 til $0005 for tom, heltal, tal, label og boolean-eller-fejl-celler, og hver krop bærer et tre-byte attributfelt, hvor senere versioner sætter et udvidet formatindeks. BIFF3 og BIFF4 opgiver det og genbruger BIFF5-postnumrene og -layoutene, $0201, $0203, $0204 og $0205, med et to-byte XF-indeks
Den sidste detalje forårsager en specifik og let fejldiagnosticerbar fejl. En BIFF3- eller BIFF4-LABEL-post er strukturelt identisk med sin BIFF5-modpart, række og kolonne efterfulgt af formatindekset og derefter karaktertallet. Skriv en læser, der antager BIFF2-layoutet, og den læser to bytes for få, går derefter ud over slutningen af posten og fejlfortolker alt derefter. Symptomet er ikke en undtagelse; det er en arbejdsbog, der læses med plausibelt skrammel i sig
Formelposter optager en parallel nummerering på tværs af alle tre, $0006, $0206 og $0406. Når en formel producerer et strengresultat, ankommer den streng i en separat efterfølgende post, $0007 eller $0207, og BIFF2-formen af den bruger et enkelt-byte længdepræfiks frem for det to-byte, der bruges senere
Hvorfor formler kommer tilbage som værdier, ikke som tekst
HotXLS læser det cachede resultat af en formel i disse filer og forsøger ikke at genopbygge formeludtrykket. Dette er en bevidst grænse, ikke et hul, der venter på at blive udfyldt
Det parsede udtryk i BIFF2 til BIFF4 bruger en token-kodning, der adskiller sig fra BIFF5 og senere på måder, der går ud over det kosmetiske: token-længder er præfikset forskelligt, referencetokens har forskellige størrelser, og funktionsindekstabellerne blev omnummereret mellem generationerne. At køre de bytes gennem en BIFF8-udtryksoversætter producerer ikke en forkert formel, den producerer en tilfældig en. At læse den cachede værdi giver dig det tal eller den streng, som Excel sidst beregnede, hvilket er, hvad en arkivmigration rent faktisk har brug for
Den cachede værdi bor ved en generationsafhængig forskydning inde i posten: byte 7 for BIFF2 og byte 6 for BIFF3 og BIFF4. Specielle værdier, strenge, booleans, fejl og tomme, er kodet i et markørord på $FFFF med en diskriminator, den samme konvention senere BIFF-generationer beholdt
At åbne en
Den kaldende kode er upåfaldende, hvilket er pointen. Registrering sker inde i 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[] er 1-baseret
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;
Bemærk indeksaritmetikken i den løkke. UsedRange-grænser er nul-baserede, mens både arksamlingen og celleadgangen er én-baserede, en inkonsistens, der går forud for det nuværende API, og som er bevaret af kompatibilitetshensyn. At glemme justeringen reviderer det forkerte rektangel og rapporterer intet usædvanligt, mens den gør det. Billige forudgående tjek, der undgår at indlæse en fil overhovedet, er beskrevet i letvægts-arbejdsbogsinspektion
Hvad du ikke får, og hvad du kan gøre ved det
Formatering fortolkes ikke. HotXLS parser ikke XF- og FONT-posterne fra disse generationer, så skrifttyper, farver, kanter og talformater er utilgængelige, og celler, som Excel engang viste som datoer, kommer tilbage som deres rå serienumre
Den sidste skal håndteres i din egen kode frem for i læseren, og grunden er ærlig: talformater i BIFF2 til BIFF4 er ikke pålidelige nok til at drive en automatisk datobeslutning. En kolonne med femcifrede tal kan være datoer, eller kan være varenumre. Konvertér bevidst, ved hjælp af arbejdsbogens datosystem, hvis regler er beskrevet i seriedatoer, 1904-systemet og talformater:
// Beslut pr. kolonne, aldrig pr. værdi: et femcifret tal kan være en
// dato eller et varenummer, og det legacy-format vil ikke fortælle dig det
if ColumnHoldsDates(C) then
begin
// De to datosystemer ligger 1462 dage fra hinanden, så den samme
// serie betegner to datoer fire år fra hinanden. Læs systemet fra
// arbejdsbogen frem for at antage ét
if Book.Date1904 then
Writeln(DateToStr(SerialToDate1904(V)))
else
Writeln(DateToStr(SerialToDate1900(V)));
end
else
Writeln(VarToStr(V));
To strukturelle bemærkninger fuldender billedet. Adgangskodebeskyttelse og kodesideposter optræder inde i den enkelte regnearksstrøm frem for i en strøm på arbejdsbogsniveau, fordi der ikke findes nogen strøm på arbejdsbogsniveau at putte dem i, så de skal genkendes i regnearkskontekst. Og en BIFF2- til BIFF4-fil indeholder præcis én arkdelstrøm; arbejdsbøger med flere ark eksisterede ikke, før formatet fik sin container
Den pragmatiske migrationssti er derfor en to-trins en: læs legacy-filen for dens værdier, og skriv derefter en moderne arbejdsbog, der bærer disse værdier med formatering, du selv anvender. Legacy-læsning, moderne skrivning og alt derimellem kører i ét bibliotek til Delphi og C++Builder, beskrevet på HotXLS Delphi regnearkskomponentsiden