Teknisk artikkel

Liste arknavn raskt i Delphi med HotXLS GetSheetNames

Noen ganger er det eneste spørsmålet en mottaksrutine trenger svar på, strukturelt: har denne arbeidsboken et ark som heter "Mapping", eller hvor mange faner har den. Å svare på det ved å kalle Open er den kostbare måten å gjøre det på. En full åpning blåser opp den delte strengtabellen, dekoder hver eneste stilpost, og går gjennom cellene i hvert ark, fordi den ikke har noen måte å vite at du bare ville ha innholdsfortegnelsen. På en stor fil er det hundrevis av megabyte med allokeringer og flere sekunder med CPU-tid brukt på å lese en liste som opptar noen få kilobyte. HotXLS, det native Delphi-regnearkbiblioteket fra losLab, gir deg den listen på egen hånd: GetSheetNames gir tilbake arknavnene, i arbeidsbokens rekkefølge, uten å materialisere en eneste celle

Hvorfor katalogen er billig å lese

Begge regnearkformatene plasserer innholdsfortegnelsen sin nær begynnelsen, og det er det som gjør et listekall raskt fremfor smart. En OOXML-pakke holder arkkatalogen i xl/workbook.xml, en del som forblir liten enten arbeidsboken har ti rader eller ti millioner. En BIFF8 .xls lagrer sine BoundSheet-poster helt i starten av arbeidsbokens globale strøm, foran all celledata. Så arbeidet et listekall unngår, er ikke en avrundingsfeil mot en full åpning. Det er mesteparten av filen. Å lese katalogen koster de samme få kilobytene uansett antall rader, mens en full åpning skalerer med dataene, og på en arbeidsbok på flere megabyte løper det gapet opp i flere størrelsesordener, både i berørte byte og allokert minne

HotXLS GetSheetNames i Delphi leser bare ark-katalogen til en XLSX- eller XLS-fil, mens en full åpning går gjennom hver celle
Katalogen sitter i workbook.xml eller BoundSheet-postene, så opplisting koster noen kilobytes mens en full åpning skalerer med dataene

Den flate kostnaden er egenskapen det er verdt å designe rundt. En mottaksport bygget på GetSheetNames oppfører seg likt på en fil med 200 rader og en på 200 MB, slik at den tregeste filen i en batch ikke lenger setter tempoet for å avgjøre om en fil i det hele tatt er verdt å behandle

Ett kall på tvers av .xls, .xlsx og malformatene

På XLS-fasaden leser TXLSWorkbook.GetSheetNames mer enn bare .xls. Den godtar også de zip-baserte .xlsx, .xlsm, .xltx og .xltm, og henter bare workbook.xml ut av arkivet. For ekte .xls-inndata skanner den BoundSheet-poster og stopper ved den første EOF-posten i den globale delstrømmen, slik at en stor binærfil fortsatt bare koster de innledende kilobytene. XLSX-fasaden bærer en garanti som betyr mer for langtidskjørende tjenestekode enn det først virker: TXLSXWorkbook.GetSheetNames lar arbeidsbokinstansen verken tilbakestilles eller fylles, slik at en instans som allerede holder et åpent dokument, kan sondere andre filer uten å forstyrre den som allerede er i hånden. GetODSSheetNames bruker samme tilnærming på OpenDocument-pakker, og hvert eneste av disse kallene har en strømoverlasting, som lar deg inspisere en opplasting som aldri havner på 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;

Det samme kallet gir en god importdialog for skrivebordet. List opp arkene, la brukeren velge ett, og betal for den fulle åpningen først etter at valget er gjort. Med en arbeidsbok på femti ark er forskjellen synlig: en velger som dukker opp momentant, mot en som stopper opp mens hele filen lastes bak den

Makroaktiverte .xlsm-filer og malformatene listes akkurat som en vanlig .xlsx, siden katalogen ligger i den samme workbook.xml uansett om en vbaProject.bin følger med i pakken eller ikke. En mottakspipeline kan derfor liste opp arkene til en makroarbeidsbok for ruting, uten noensinne å røre makronyttelasten eller gjøre noe som ville kjørt den, og overlate makropolicybeslutningen til trinnet som faktisk åpner filen

Å lese returverdien uten å lure seg selv

Returkonvensjonene er ikke ensartede på tvers av HotXLS. Noen kall returnerer 1 ved suksess, andre returnerer et antall, så for listefunksjonene er den eneste kontrollen som holder, å behandle enhver verdi på null eller lavere som en feil, med strenglisten tømt. Stå imot fristelsen til å lese en tom liste som "en arbeidsbok uten ark". Både ECMA-376 og BIFF8-spesifikasjonen krever minst ett ark i en gyldig arbeidsbok, så null navn betyr alltid at lesingen mislyktes, aldri at filen legitimt er tom

En mislykket listing er i seg selv et signal verdt å beholde. En .xlsx-fil som feiler ved kallet, er én av noen få spesifikke ting: avkuttet, egentlig ikke en OOXML-pakke i det hele tatt (feilmerkede CSV-eksporter fra andre systemer dukker konstant opp her), eller en kryptert beholder. Å skille disse fra hverandre er jobben til neste kontroll. Å logge de første bytene av den avviste filen sammen med feilen gjør som regel en supporttråd om til én enkelt melding

Å oppdage krypterte beholdere før du ruter

En kryptert .xlsx er ikke en zip. Det er en OLE-sammensatt fil som pakker inn EncryptionInfo- og EncryptedPackage-strømmer, så GetSheetNames kan ikke se inn i den og returnerer feil som enhver annen uleselig fil. CanReadEncrypted tester for nettopp den beholderformen, som lar mottaket rute en kryptert fil med hensikt, fremfor å svelge en generisk lesefeil et sted dypt inne i en arbeidsprosess:

Delphi inntaks-triageflyt med HotXLS CanReadEncrypted og GetSheetNames som ruter opplastinger til trenger-passord, uleselig, eller normal
CanReadEncrypted kjører først fordi en kryptert OOXML-fil er en OLE-beholder som opplistingskallene ikke kan se inn i
type
  TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);

function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // Kryptert OOXML er en OLE-beholder, ikke en zip: sjekk først,
    // fordi listekallene ikke kan se inn i den.
    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;

Kryptering er der HotXLS er bevisst asymmetrisk, så rutingen må respektere det. Gammel .xls-kryptering (RC4, RC4 CryptoAPI, XOR) er leselig: TXLSWorkbook.Open(FileName, Password) dekrypterer med et lagret passord, og de filene kan bli værende på den automatiserte stien. Krypterte OOXML-pakker går motsatt vei. HotXLS kan skrive en med SaveAsEncrypted, men den kan ikke lese en tilbake. OpenEncrypted kaster EXlsxEncryptionNotImplemented når den mottar en kryptert pakke, og det er derfor et ærlig mottaksdesign sender krypterte .xlsx til en person med Excel og holder den passordbærende .xls-en i kode

For batcharbeid fortjener denne klassifisereren plassen sin ved å kjøre over en hel innkommende mappe før noen arbeidsprosess starter reell behandling, siden hver sondering koster omtrent én filåpning og noen få kilobyte med lesing. Å legge den tidlig i løypa endrer feilmodusen driften faktisk bryr seg om. I stedet for at en jobb klokken tre om natten dør på fil 412 av 600, får du 412 filer i kø og 5 avvist ved mottak med en årsak festet til hver. Samme bibliotekkall, langt bedre driftshistorie

Spørsmålene et listekall ikke kan svare på

Navn og rekkefølge er alt du får. Listekallene sier ingenting om synlighet, så skjulte og svært skjulte ark ankommer listen og ser ut som alle andre. De rapporterer ingen brukte-område-dimensjoner, ingen celletall, og ingen dokumentegenskaper. docProps/core.xml-delen er også liten, men det finnes ingen egenskaper-bare-sondering i dag, så forfatter- og tittelmetadata koster fortsatt en full Open. Den ryddige måten å leve med det på, er å la de billige fakta rute hver fil og reservere de dyre for filer som overlever rutingen. For filene som faktisk går videre til en dyp lesing, kjører en skrivebeskyttet skanning av en stor .xls merkbart raskere med _DisableGraphics := True, som hopper over OfficeArt-parsing. Bare aldri lagre fra den instansen: tegnelaget den hoppet over, er borte fra modellen, og lagring ville droppet det fra filen

Filer som består triageringen, går som regel videre til dypere analyse. arbeidsbenken for arbeidsbokrevisjon og -konvertering dekker de tellerne per ark det er verdt å samle inn når en full åpning først er berettiget, og ytelsesveiledningen for store arbeidsbøker dekker hvordan man holder den fulle åpningen rask

HotXLS er et nativt Object Pascal-regnearkbibliotek for Delphi og C++Builder; hele API-overflaten, inkludert inspeksjonskallene vist her, er dokumentert på HotXLS Delphi Component-produktsiden