Tehnički članak

Brzo izlistavanje naziva listova u Delphi-ju uz HotXLS GetSheetNames

Ponekad je jedino pitanje na koje dolazna rutina treba da odgovori strukturno: da li ova radna sveska ima list pod nazivom "Mapping" ili koliko kartica (tabs) sadrži. Odgovaranje na to pozivanjem metode Open je skup način za rešavanje problema. Potpuno otvaranje naduvava deljenu tabelu stringova, dekodira svaki zapis stila i prolazi kroz ćelije svakog radnog lista, jer nema načina da zna da ste želeli samo sadržaj. Na velikoj datoteci, to podrazumeva stotine megabajta alokacija i nekoliko sekundi procesorskog vremena potrošenog na čitanje liste koja zauzima svega nekoliko kilobajta. HotXLS, izvorna Delphi biblioteka za radne tabele kompanije losLab, pruža vam tu listu samostalno: GetSheetNames vraća nazive radnih listova, u redosledu radne sveske, bez kreiranja ijedne ćelije

Zašto je katalog jeftin za čitanje

Oba formata radnih tabela stavljaju svoj sadržaj blizu početka, što poziv za izlistavanje čini brzim, a ne komplikovanim. OOXML paket čuva katalog listova u xl/workbook.xml, delu koji ostaje mali bez obzira na to da li radna sveska sadrži deset redova ili deset miliona. BIFF8 .xls čuva svoje BoundSheet zapise na početku toka globalnih podataka radne sveske, pre bilo kakvih podataka ćelija. Dakle, posao koji poziv za izlistavanje izbegava nije greška zaokruživanja u odnosu na potpuno otvaranje. To je zapravo najveći deo datoteke. Čitanje kataloga košta isto nekoliko kilobajta bez obzira na broj redova, dok se potpuno otvaranje skalira sa podacima, a na radnoj svesci od nekoliko megabajta ta razlika dostiže nekoliko redova veličine i u pogledu dodirnutih bajtova i u pogledu alocirane memorije

Taj fiksni trošak je svojstvo oko kojeg vredi dizajnirati rešenje. Dolazni filter izgrađen na GetSheetNames ponaša se isto na datoteci od 200 redova i onoj od 200 MB, tako da najsporija datoteka u grupi više ne diktira tempo za donošenje odluke da li uopšte vredi obrađivati datoteku

Jedan poziv za .xls, .xlsx i formate šablona

Na XLS fasadi, TXLSWorkbook.GetSheetNames čita više od samog .xls-a. Takođe prihvata zipovane formate .xlsx, .xlsm, .xltx i .xltm, izvlačeći samo workbook.xml iz arhive. Za izvorni .xls ulaz, on skenira BoundSheet zapise i zaustavlja se na prvom EOF zapisu toka globalnih podataka, tako da velika binarna datoteka i dalje košta samo početnih nekoliko kilobajta. XLSX fasada nosi garanciju koja je važnija za dugotrajni servisni kod nego što se na prvi pogled čini: TXLSXWorkbook.GetSheetNames ostavlja instancu radne sveske niti resetovanom niti popunjenom, tako da instanca koja već drži otvoren dokument može ispitivati druge datoteke bez remećenja onog koji se trenutno obrađuje. GetODSSheetNames primenjuje isti pristup na OpenDocument pakete, a svaki od ovih poziva ima preopterećenje toka (stream overload), što vam omogućava da pregledate otpremljenu datoteku koja nikada ne sleće na disk

var
  Book: TXLSXWorkbook;
  Names: TStringList;
  I: Integer;
begin
  Names := TStringList.Create;
  Book := TXLSXWorkbook.Create;
  try
    if Book.GetSheetNames('upload-7f3a.xlsx', Names) <= 0 then
      raise Exception.Create('unreadable workbook package');
    if Names.IndexOf('Mapping') < 0 then
      raise Exception.Create('required Mapping sheet is missing');
    for I := 0 to Names.Count - 1 do
      Writeln(Format('sheet %d: %s', [I, Names[I]]));
  finally
    Book.Free;
    Names.Free;
  end;
end;

Isti poziv čini dobar uvozni dijalog za desktop aplikacije. Izlistajte listove, pustite korisnika da izabere jedan, i platite cenu potpunog otvaranja tek nakon što se izbor napravi. Kod radne sveske sa pedeset listova razlika je vidljiva: selektor koji se pojavljuje odmah nasuprot onom koji zastajkuje dok se cela datoteka učitava u pozadini

Datoteke sa omogućenim makroima (.xlsm) i formati šablona se izlistavaju potpuno isto kao i običan .xlsx, pošto se katalog nalazi u istom workbook.xml bez obzira na to da li se vbaProject.bin nalazi u paketu ili ne. Dolazni proces obrade stoga može popisati listove radne sveske sa makroima radi usmeravanja, nikada ne dotičući sam makro kod niti radeći bilo šta što bi ga pokrenulo, i prepustiti poziv politike makroa fazi koja zapravo otvara datoteku

Čitanje povratne vrednosti bez samozavaravanja

Konvencije vraćanja vrednosti nisu ujednačene u HotXLS-u. Neki pozivi vraćaju 1 u slučaju uspeha, drugi vraćaju ukupan broj, tako da je za funkcije izlistavanja jedina ispravna provera tretiranje bilo koje vrednosti koja je nula ili manja kao neuspeha, pri čemu se lista stringova prazni. Oduprite se iskušenju da pročitate praznu listu kao "radnu svesku bez listova". Standard ECMA-376 i BIFF8 specifikacija zahtevaju najmanje jedan list u važećoj radnoj svesci, tako da nula naziva uvek znači da čitanje nije uspelo, a nikada da je datoteka legitimno prazna

Neuspelo izlistavanje je samo po sebi signal koji vredi zadržati. .xlsx datoteka koja ne uspe na ovom pozivu jeste jedna od nekoliko specifičnih stvari: skraćena (truncated), uopšte nije OOXML paket (pogrešno označeni CSV izvozi iz drugih sistema se stalno pojavljuju ovde) ili šifrovani kontejner. Razlikovanje tih slučajeva je zadatak sledeće provere. Beleženje prvih bajtova odbijene datoteke pored neuspeha obično pretvara dugačku nit tehničke podrške u jednu jedinu poruku

Detekcija šifrovanih kontejnera pre usmeravanja

Šifrovani .xlsx nije zip. To je OLE složena datoteka (OLE compound file) koja obmotava EncryptionInfo i EncryptedPackage tokove, tako da GetSheetNames ne može videti unutrašnjost i vraća neuspeh kao i svaka druga nečitljiva datoteka. Metoda CanReadEncrypted proverava taj oblik kontejnera, što omogućava dolaznom procesu da namerno usmeri šifrovanu datoteku umesto da proguta generičku grešku pri čitanju odnekud iz dubine radnog procesa:

type
  TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);

function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // Encrypted OOXML is an OLE container, not a zip: check first,
    // because the listing calls cannot look inside it.
    if Book.CanReadEncrypted(FileName) then
      Exit(irNeedsPassword);
    if SameText(ExtractFileExt(FileName), '.ods') then
    begin
      if Book.GetODSSheetNames(FileName, Names) <= 0 then
        Exit(irUnreadable);
    end
    else if Book.GetSheetNames(FileName, Names) <= 0 then
      Exit(irUnreadable);
    Result := irNormal;
  finally
    Book.Free;
  end;
end;

Šifrovanje je oblast u kojoj je HotXLS namerno asimetričan, tako da usmeravanje mora to da poštuje. Staro .xls šifrovanje (RC4, RC4 CryptoAPI, XOR) je čitljivo: TXLSWorkbook.Open(FileName, Password) dešifruje sa sačuvanom lozinkom, i te datoteke mogu ostati na automatizovanoj putanji. Šifrovani OOXML paketi idu drugim putem. HotXLS može da napiše jedan pomoću SaveAsEncrypted, ali ga ne može pročitati nazad. Poziv OpenEncrypted podiže izuzetak EXlsxEncryptionNotImplemented kada mu se prosledi šifrovani paket, zbog čega pošten dizajn dolaznog filtera šalje šifrovani .xlsx čoveku sa Excel-om, a .xls koji nosi lozinku zadržava u kodu

Za grupni rad, ovaj klasifikator zarađuje svoje mesto prolaskom kroz čitav dolazni direktorijum pre nego što bilo koji radni proces započne stvarnu obradu, pošto svaka sonda košta otprilike jedno otvaranje datoteke i nekoliko kilobajta čitanja. Postavljanje ovoga na sam početak menja način neuspeha do kojeg je operativnom timu zapravo stalo. Umesto da posao u 3 ujutru pukne na datoteci 412 od 600, dobijate 412 datoteka u redu za čekanje i 5 odbijenih na ulazu sa priloženim razlogom za svaku. Isti pozivi biblioteke, daleko bolja operativna priča

Pitanja na koja poziv za izlistavanje ne može da odgovori

Nazivi i redosled su sve što dobijate. Pozivi za izlistavanje ne govore ništa o vidljivosti, tako da skriveni i vrlo-skriveni listovi stižu na listu izgledajući kao i svi drugi. Oni ne prijavljuju dimenzije iskorišćenog opsega, broj ćelija niti svojstva dokumenta. Odeljak docProps/core.xml je takođe mali, ali danas ne postoji sonda samo za svojstva, tako da metapodaci o autoru i naslovu i dalje koštaju punog poziva Open. Čist način da živite sa tim jeste da pustite jeftine činjenice da usmere svaku datoteku, a skupe rezrevišete za datoteke koje prežive to usmeravanje. Za datoteke koje nastave ka dubokom čitanju, skeniranje velike .xls datoteke samo za čitanje radi znatno brže uz _DisableGraphics := True, što preskače parsiranje OfficeArt-a. Samo nikada nemojte čuvati iz te instance: grafički sloj koji je preskočen je nestao iz modela, i čuvanje bi ga izbrisalo iz datoteke

Datoteke koje prođu trijažu obično idu na dublju analizu. Članak o reviziji radne sveske i radnom stolu za konverziju pokriva brojače po listu koje vredi prikupiti kada se opravda potpuno otvaranje, a vodič za performanse velikih radnih svezaka pokriva kako da to potpuno otvaranje ostane brzo

HotXLS je izvorna Object Pascal biblioteka za radne tabele za Delphi i C++Builder; kompletna površina API-ja, uključujući pozive za inspekciju prikazane ovde, dokumentovana je na stranici proizvoda HotXLS komponente