HotXLS otvara radne sveske napisane Excelom 2.0, 3.0 i 4.0 direktno iz Delphija i C++Buildera. Ovi fajlovi prethode OLE kompund dokument kontejneru koji svaki kasniji .xls koristi, pa su sirovi tokovi BIFF zapisa bez ikakvog omotača skladišta, a čitač izgrađen za BIFF8 neće pronaći nijednu prepoznatljivu strukturu unutar njih. Otvaranje jednog koristi isti poziv Open kao svaka druga radna sveska; čitač otkriva format i menja putanju
Ovi fajlovi se i dalje pojavljuju, što je jedini razlog zbog kog je bilo šta od ovoga bitno. Inženjerske arhive, čuvanje državnih evidencija, laboratorijski podaci sa instrumenata čiji je kontrolni softver pisan 1993, i dugotrajni računovodstveni sistemi svi su za sobom ostavili BIFF2 i BIFF4 radne sveske. Moderni Excel otvoreno odbija da otvori nekoliko njih, pošto je uklonio nasleđene konvertore iz bezbednosnih razloga, što ostavlja skup podataka koji niko ne može pročitati alatom koji iko poseduje
Šta čini pre-OLE radnu svesku drugačijom?
Svaki .xls od Excela 5.0 naovamo je OLE2 kompund fajl, mali fajl sistem unutar fajla, gde radna sveska živi u toku nazvanom Workbook ili Book. Parsiranje jednog počinje parsiranjem tog kontejnera, kao što je opisano u binarnom formatu kompund fajla u Pascalu
BIFF2 do BIFF4 nemaju kontejner. Fajl počinje odmah BOF zapisom, a broj zapisa tog BOF-a kodira generaciju: $0009 za BIFF2, $0209 za BIFF3 i $0409 za BIFF4. HotXLS validira dužinu tela BOF-a, koja je između četiri i šest bajtova, i tip podtoka, $0010 za radni list, $0020 za grafikon i $0040 za makro list, pre nego što se posveti sirovoj putanji. Ta validacija je ono što sprečava da se pokvaren ili pogrešno identifikovan fajl protumači kao vrlo stara radna sveska
Tri generacije, tri rasporeda zapisa
Zapisi ćelija su mesto gde se generacije najvidljivije razilaze. BIFF2 zauzima susedni blok niskih brojeva zapisa, $0001 do $0005 za praznu, celobrojnu, brojevnu, oznaka i bulovu-ili-grešku ćeliju, a svako telo nosi polje atributa od tri bajta gde kasnije verzije stavljaju indeks proširenog formata. BIFF3 i BIFF4 to napuštaju i ponovo koriste brojeve i rasporede zapisa BIFF5, $0201, $0203, $0204 i $0205, sa indeksom XF od dva bajta
Taj poslednji detalj izaziva specifičan i lako pogrešno dijagnostikovan otkaz. BIFF3 ili BIFF4 LABEL zapis je strukturno identičan svom BIFF5 pandanu, red i kolona praćeni indeksom formata, a zatim brojem karaktera. Napišite čitač koji pretpostavlja raspored BIFF2 i on pročita dva bajta premalo, zatim ošeta van kraja zapisa i pogrešno protumači sve nakon toga. Simptom nije izuzetak; to je radna sveska koja se čita sa uverljivim smećem u sebi
Zapisi formula zauzimaju paralelno numerisanje kroz sve tri, $0006, $0206 i $0406. Kada formula proizvede rezultat u vidu stringa, taj string stiže u posebnom sledećem zapisu, $0007 ili $0207, a BIFF2 oblik toga koristi prefiks dužine od jednog bajta umesto onog od dva bajta koji se koristi kasnije
Zašto se formule vraćaju kao vrednosti, ne kao tekst
HotXLS čita keširan rezultat formule u ovim fajlovima i ne pokušava da rekonstruiše izraz formule. Ovo je namerna granica, ne praznina koja čeka da bude popunjena
Parsiran izraz u BIFF2 do BIFF4 koristi kodiranje tokena koje se razlikuje od BIFF5 i kasnijih na načine koji nadilaze kozmetiku: dužine tokena su prefiksirane drugačije, referentni tokeni imaju drugačije veličine, a tabele indeksa funkcija su ponovo numerisane između generacija. Prolazak tih bajtova kroz BIFF8 prevodilac izraza ne proizvodi pogrešnu formulu, proizvodi nasumičnu. Čitanje keširane vrednosti daje vam broj ili string koji je Excel poslednji put izračunao, što je ono što migracija arhive zapravo treba
Keširana vrednost živi na ofsetu koji zavisi od generacije unutar zapisa: bajt 7 za BIFF2 i bajt 6 za BIFF3 i BIFF4. Posebne vrednosti, stringovi, bulove vrednosti, greške i prazne vrednosti, kodirane su u markir reči $FFFF sa diskriminatorom, istom konvencijom koju su zadržale kasnije generacije BIFF-a
Otvaranje jednog
Pozivajući kod je nezapažen, što je i poenta. Detekcija se dešava unutar 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[] je bazirano na jedan
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;
Obratite pažnju na aritmetiku indeksa u toj petlji. Granice UsedRange su bazirane na nulu dok su i kolekcija listova i pristup ćelijama bazirani na jedan, nedoslednost koja prethodi trenutnom API-ju i sačuvana je radi kompatibilnosti. Zaboravljanje prilagođavanja pregleda pogrešan pravougaonik i ne prijavljuje ništa neobično dok to radi. Jeftine prethodne provere koje izbegavaju učitavanje fajla su obrađene u lakoj inspekciji radne sveske
Šta ne dobijate, i šta sa tim uraditi
Formatiranje se ne tumači. HotXLS ne parsira XF i FONT zapise ovih generacija, pa su fontovi, boje, ivice i formati brojeva nedostupni, a ćelije koje je Excel nekada prikazivao kao datume vraćaju se kao sirovi serijski brojevi
Taj poslednji zahteva rukovanje u vašem sopstvenom kodu, ne u čitaču, a razlog je iskren: formati brojeva u BIFF2 do BIFF4 nisu dovoljno pouzdani da pokrenu automatsku odluku o datumu. Kolona petocifrenih brojeva može biti datumi, ili može biti brojevi delova. Konvertujte namerno, koristeći sistem datuma radne sveske, čija su pravila opisana u serijskim brojevima datuma, 1904 sistemu i formatima brojeva:
// Odlučite po koloni, nikada po vrednosti: petocifreni broj može
// biti datum ili broj dela, a nasleđen format vam to neće reći
if ColumnHoldsDates(C) then
begin
// Dva sistema datuma su udaljena 1462 dana, pa isti serijski broj
// označava dva datuma udaljena četiri godine. Čitajte sistem iz
// radne sveske umesto da ga pretpostavljate
if Book.Date1904 then
Writeln(DateToStr(SerialToDate1904(V)))
else
Writeln(DateToStr(SerialToDate1900(V)));
end
else
Writeln(VarToStr(V));
Dve strukturne napomene upotpunjuju sliku. Zaštita lozinkom i zapisi kodne stranice se pojavljuju unutar jedinog toka radnog lista umesto u toku na nivou radne sveske, jer ne postoji tok na nivou radne sveske u koji bi ih stavili, pa moraju biti prepoznati u kontekstu radnog lista. A BIFF2 do BIFF4 fajl sadrži tačno jedan podtok lista; radne sveske sa više listova nisu postojale sve dok format nije dobio svoj kontejner
Pragmatičan put migracije je stoga dvokoračni: pročitajte nasleđen fajl radi njegovih vrednosti, zatim napišite modernu radnu svesku koja nosi te vrednosti sa formatiranjem koje sami primenite. Nasleđeno čitanje, moderno pisanje i sve između izvode se u jednoj biblioteci za Delphi i C++Builder, opisanoj na stranici HotXLS Delphi komponente za tabele