Tekninen artikkeli

Excel 2.0–4.0-tiedostojen lukeminen Delphissä HotXLS:llä

HotXLS avaa Excel 2.0:n, 3.0:n ja 4.0:n kirjoittamia työkirjoja suoraan Delphistä ja C++Builderista. Nämä tiedostot ovat vanhempia kuin OLE-yhdistelmädokumenttisäiliö, jota jokainen myöhempi .xls käyttää, joten ne ovat raakoja BIFF-tietuevirtoja ilman minkäänlaista säilykääretä, eikä BIFF8:aa varten rakennettu lukija löydä niiden sisältä yhtäkään tunnistettavaa rakennetta. Yhden avaaminen käyttää samaa Open-kutsua kuin mikä tahansa muu työkirja; lukija tunnistaa muodon ja vaihtaa polkua

Nämä tiedostot ilmestyvät edelleen, ja se on ainoa syy, miksi mikään tästä on merkityksellistä. Insinöörien arkistot, valtion asiakirjojen säilytys, laboratoriodata instrumenteista, joiden ohjausohjelmisto kirjoitettiin vuonna 1993, ja pitkään toimineet kirjanpitojärjestelmät jättivät kaikki jälkeensä BIFF2- ja BIFF4-työkirjoja. Nykyaikainen Excel kieltäytyy avaamasta useita niistä suoraan, koska se on poistanut vanhat muuntimet turvallisuussyistä, mikä jättää jälkeen tietoaineiston, jota kukaan ei voi lukea työkalulla, joka kenelläkään on

Mikä tekee OLE:a edeltävästä työkirjasta erilaisen?

Jokainen .xls-tiedosto Excel 5.0:sta lähtien on OLE2-yhdistelmätiedosto, pieni tiedostojärjestelmä tiedoston sisällä, jossa työkirja asuu virrassa nimeltä Workbook tai Book. Yhden jäsentäminen alkaa tuon säiliön jäsentämisellä, kuten kuvataan artikkelissa yhdistelmätiedoston binäärimuoto Pascalissa

BIFF2:sta BIFF4:ään ei ole säiliötä. Tiedosto alkaa välittömästi BOF-tietueella, ja tuon BOF:n tietuenumero koodaa sukupolven: $0009 BIFF2:lle, $0209 BIFF3:lle ja $0409 BIFF4:lle. HotXLS validoi BOF-rungon pituuden, joka on neljän ja kuuden tavun välillä, sekä alivirran tyypin, $0010 laskentataulukolle, $0020 kaaviolle ja $0040 makrotaulukolle, ennen kuin sitoutuu raakaan polkuun. Tämä validointi on se, mikä estää korruptoituneen tai väärin tunnistetun tiedoston tulkitsemisen hyvin vanhaksi työkirjaksi

Kolme sukupolvea, kolme tietueasettelua

Solutietueet ovat kohta, jossa sukupolvet eroavat toisistaan näkyvimmin. BIFF2 varaa yhtenäisen lohkon matalia tietuenumeroita, $0001:sta $0005:een tyhjille, kokonaisluku-, numero-, otsikko- ja totuusarvo-tai-virhesoluille, ja jokainen runko kantaa kolmitavuisen attribuuttikentän kohdassa, johon myöhemmät versiot laittavat laajennetun muotoindeksin. BIFF3 ja BIFF4 hylkäävät tämän ja käyttävät uudelleen BIFF5:n tietuenumeroita ja asetteluja, $0201, $0203, $0204 ja $0205, kaksitavuisella XF-indeksillä

Tämä viimeinen yksityiskohta aiheuttaa tietyn ja helposti väärin diagnosoitavan virheen. BIFF3- tai BIFF4-LABEL-tietue on rakenteeltaan identtinen BIFF5-vastineensa kanssa, rivi ja sarake, joita seuraa muotoindeksi ja sitten merkkimäärä. Kirjoita lukija, joka olettaa BIFF2-asettelun, ja se lukee kaksi tavua liian vähän, kävelee sitten tietueen lopun yli ja tulkitsee väärin kaiken sen jälkeisen. Oire ei ole poikkeus; se on työkirja, joka luetaan uskottavan roskan kanssa

Kaavatietueet varaavat rinnakkaisen numeroinnin kaikkien kolmen yli, $0006, $0206 ja $0406. Kun kaava tuottaa merkkijonotuloksen, tämä merkkijono saapuu erillisessä seuraavassa tietueessa, $0007 tai $0207, ja sen BIFF2-muoto käyttää yksitavuista pituusetuliitettä myöhemmin käytetyn kaksitavuisen sijaan

Miksi kaavat palautuvat arvoina, ei tekstinä

HotXLS lukee kaavan välimuistiin tallennetun tuloksen näissä tiedostoissa eikä yritä rakentaa kaavalauseketta uudelleen. Tämä on tietoinen raja, ei aukko, joka odottaa täyttämistä

BIFF2:sta BIFF4:ään jäsennetty lauseke käyttää tokenikoodausta, joka eroaa BIFF5:sta ja myöhemmistä tavoilla, jotka menevät kosmetiikkaa syvemmälle: tokenien pituudet on etuliitetty eri tavalla, viittaustokeneilla on eri koot, ja funktioindeksitaulut numeroitiin uudelleen sukupolvien välillä. Näiden tavujen ajaminen BIFF8-lausekekääntäjän läpi ei tuota väärää kaavaa, se tuottaa satunnaisen. Välimuistiin tallennetun arvon lukeminen antaa sen luvun tai merkkijonon, jonka Excel viimeksi laski, mikä on juuri sitä, mitä arkistomigraatio todella tarvitsee

Välimuistiin tallennettu arvo asuu sukupolvesta riippuvassa siirtymässä tietueen sisällä: tavu 7 BIFF2:lle ja tavu 6 BIFF3:lle ja BIFF4:lle. Erikoisarvot, merkkijonot, totuusarvot, virheet ja tyhjät, koodataan merkintäsanaan $FFFF erottelijan kanssa, saman käytännön, jonka myöhemmät BIFF-sukupolvet säilyttivät

Yhden avaaminen

Kutsuva koodi on huomaamaton, ja juuri se on pointti. Tunnistus tapahtuu Open-metodin sisällä:

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[] on 1-pohjainen
    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;

Huomaa tuossa silmukassa oleva indeksiaritmetiikka. UsedRange-rajat ovat nollapohjaisia, kun taas sekä taulukkokokoelma että solujen käyttö ovat ykköspohjaisia, epäjohdonmukaisuus, joka on vanhempi kuin nykyinen API ja säilytetään yhteensopivuuden vuoksi. Säädön unohtaminen tarkastaa väärän suorakulmion eikä samalla raportoi mitään epätavallista. Halvat esitarkistukset, jotka välttävät tiedoston lataamisen kokonaan, käsitellään artikkelissa kevyt työkirjan tarkastus

Mitä et saa ja mitä sille voi tehdä

Muotoilua ei tulkita. HotXLS ei jäsennä näiden sukupolvien XF- ja FONT-tietueita, joten fontit, värit, reunukset ja lukumuodot eivät ole käytettävissä, ja solut, jotka Excel kerran näytti päivämäärinä, palautuvat raakoina sarjanumeroinaan

Tämä viimeinen asia täytyy käsitellä omassa koodissasi eikä lukijassa, ja syy on rehellinen: lukumuodot BIFF2:sta BIFF4:ään eivät ole tarpeeksi luotettavia ohjaamaan automaattista päivämääräpäätöstä. Sarake viisinumeroisia lukuja voi olla päivämääriä, tai ne voivat olla osanumeroita. Muunna harkitusti käyttäen työkirjan päivämääräjärjestelmää, jonka säännöt kuvataan artikkelissa päivämäärän sarjanumerot, 1904-järjestelmä ja lukumuodot:

// Päätä sarakekohtaisesti, ei koskaan arvokohtaisesti: viisinumeroinen luku voi olla
// päivämäärä tai osanumero, eikä vanha muoto kerro sitä sinulle
if ColumnHoldsDates(C) then
begin
  // Kaksi päivämääräjärjestelmää ovat 1462 päivän päässä toisistaan, joten sama sarjanumero
  // tarkoittaa kahta päivämäärää, jotka ovat neljän vuoden päässä toisistaan. Lue järjestelmä
  // työkirjasta olettamisen sijaan
  if Book.Date1904 then
    Writeln(DateToStr(SerialToDate1904(V)))
  else
    Writeln(DateToStr(SerialToDate1900(V)));
end
else
  Writeln(VarToStr(V));

Kaksi rakenteellista huomiota täydentää kuvan. Salasanasuojaus- ja koodisivutietueet esiintyvät yhden laskentataulukon virran sisällä työkirjatason virran sijaan, koska ei ole olemassa työkirjatason virtaa, johon ne voisi laittaa, joten ne täytyy tunnistaa laskentataulukon kontekstissa. Ja BIFF2:sta BIFF4:ään tiedosto sisältää täsmälleen yhden taulukon alivirran; monitaulukkoisia työkirjoja ei ollut olemassa ennen kuin muoto sai säiliönsä

Käytännöllinen migraatiopolku on siis kaksivaiheinen: lue vanha tiedosto sen arvoja varten, kirjoita sitten nykyaikainen työkirja, joka kantaa nuo arvot itse soveltamallasi muotoilulla. Vanhojen tiedostojen lukeminen, nykyaikainen kirjoittaminen ja kaikki niiden välillä toimivat yhdessä kirjastossa Delphille ja C++Builderille, kuvattuna sivulla HotXLS Delphi -laskentataulukkokomponentin sivulla