Ibland är den enda frågan en intagsrutin behöver ha svar på strukturell: har den här arbetsboken ett blad som heter "Mapping", eller hur många flikar bär den. Att besvara det genom att anropa Open är det dyra sättet att göra det. En fullständig öppning blåser upp tabellen med delade strängar, avkodar varje stilpost och går igenom varje kalkylblads celler, eftersom den inte har något sätt att veta att du bara ville ha innehållsförteckningen. På en stor fil är det hundratals megabyte av allokeringar och flera sekunders CPU-tid för att läsa en lista som upptar några kilobyte. HotXLS, det nativa Delphi-kalkylbiblioteket från losLab, ger dig den listan på egen hand: GetSheetNames lämnar tillbaka kalkylbladsnamnen, i arbetsboksordning, utan att materialisera en enda cell
Varför katalogen är billig att läsa
Båda kalkylbladsformaten placerar sin innehållsförteckning nära början, vilket är vad som gör ett listningsanrop snabbt snarare än smart. Ett OOXML-paket håller kalkylbladskatalogen i xl/workbook.xml, en del som förblir liten oavsett om arbetsboken innehåller tio rader eller tio miljoner. En BIFF8 .xls lagrar sina BoundSheet-poster i början av arbetsbokens globala ström, före all celldata. Så arbetet ett listningsanrop undviker är inte en avrundningsfel jämfört med en fullständig öppning. Det är merparten av filen. Att läsa katalogen kostar samma handfull kilobyte oavsett radantal, medan en fullständig öppning skalar med datan, och på en flera megabyte stor arbetsbok sträcker sig det gapet till flera storleksordningar, både i berörda byte och allokerat minne
Den platta kostnaden är egenskapen värd att designa kring. En intagsspärr byggd på GetSheetNames beter sig likadant på en fil med 200 rader och en på 200 MB, så den långsammaste filen i en batch sätter inte längre takten för att avgöra om en fil ens är värd att bearbeta
Ett anrop över .xls, .xlsx och mallformaten
På XLS-fasaden läser TXLSWorkbook.GetSheetNames mer än .xls. Den accepterar också den zip-baserade .xlsx, .xlsm, .xltx och .xltm, och drar bara ut workbook.xml ur arkivet. För genuin .xls-indata skannar den BoundSheet-poster och stannar vid den första EOF-posten i den globala underströmmen, så en stor binär fil kostar fortfarande bara sina öppningskilobyte. XLSX-fasaden bär en garanti som spelar större roll för långkörande tjänstekod än den först verkar göra: TXLSXWorkbook.GetSheetNames lämnar arbetsboksinstansen varken återställd eller ifylld, så en instans som redan håller ett öppet dokument kan sondera andra filer utan att störa den till hands. GetODSSheetNames tillämpar samma angreppssätt på OpenDocument-paket, och vart och ett av de här anropen har en strömöverlagring, vilket låter dig inspektera en uppladdning som aldrig landar 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;
Samma anrop gör en bra skrivbordsimportdialog. Lista bladen, låt användaren välja ett, och betala för den fullständiga öppningen först efter att valet är gjort. Med en femtioblads arbetsbok är skillnaden synlig: en väljare som visas direkt jämfört med en som stannar upp medan hela filen laddas bakom den
Makroaktiverade .xlsm-filer och mallformaten listas exakt som en vanlig .xlsx, eftersom katalogen sitter i samma workbook.xml oavsett om en vbaProject.bin följer med i paketet eller inte. En intagspipeline kan därför räkna upp bladen i en makroarbetsbok för routning, utan att någonsin röra makronyttolasten och utan att göra något som skulle köra den, och lämna makropolicybeslutet till steget som faktiskt öppnar filen
Att läsa returvärdet utan att lura dig själv
Returkonventioner är inte enhetliga över HotXLS. Vissa anrop returnerar 1 vid framgång, andra returnerar ett antal, så för listningsfunktionerna är den enda kontroll som håller att behandla vilket värde som helst noll eller lägre som ett misslyckande, med stränglistan tömd. Motstå frestelsen att läsa en tom lista som "en arbetsbok utan blad." Både ECMA-376 och BIFF8-specifikationen kräver minst ett blad i en giltig arbetsbok, så noll namn betyder alltid att läsningen misslyckades, aldrig att filen legitimt är tom
En misslyckad listning är i sig en signal värd att behålla. En .xlsx-fil som misslyckas med anropet är en av några specifika saker: trunkerad, egentligen inte ett OOXML-paket alls (felmärkta CSV-exporter från andra system dyker upp här ständigt), eller en krypterad behållare. Att skilja de åt är nästa kontrolls jobb. Att logga de första byten av den avvisade filen tillsammans med felet förvandlar vanligtvis en supporttråd till ett enda meddelande
Att upptäcka krypterade behållare innan du routar
En krypterad .xlsx är inte en zip. Det är en OLE-sammansatt fil som omsluter EncryptionInfo- och EncryptedPackage-strömmar, så GetSheetNames kan inte se in i den och returnerar misslyckande precis som vilken annan oläsbar fil som helst. CanReadEncrypted testar för den behållarformen, vilket låter intaget routa en krypterad fil avsiktligt i stället för att sluka ett generiskt läsfel från någonstans djupt i en arbetare:
type
TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);
function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
// Krypterad OOXML är en OLE-behållare, inte en zip: kontrollera först,
// eftersom listningsanropen inte kan titta in 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 är där HotXLS avsiktligt är asymmetriskt, så routningen måste respektera det. Äldre .xls-kryptering (RC4, RC4 CryptoAPI, XOR) går att läsa: TXLSWorkbook.Open(FileName, Password) dekrypterar med ett lagrat lösenord, och de filerna kan stanna på den automatiserade vägen. Krypterade OOXML-paket går åt andra hållet. HotXLS kan skriva ett med SaveAsEncrypted, men kan inte läsa ett tillbaka. OpenEncrypted kastar EXlsxEncryptionNotImplemented när det ges ett krypterat paket, vilket är varför en ärlig intagsdesign skickar krypterad .xlsx till en människa med Excel och behåller den lösenordsbärande .xls i kod
För batcharbete förtjänar den här klassificeraren sin plats genom att köras över en hel inkommande katalog innan någon arbetare påbörjar riktig bearbetning, eftersom varje sondering kostar ungefär en filöppning och några kilobyte läsningar. Att lägga den i förväg ändrar det felläge drift faktiskt bryr sig om. I stället för att ett jobb klockan tre på morgonen dör på fil 412 av 600, får du 412 filer köade och 5 avvisade vid intaget med en anledning kopplad till var och en. Samma biblioteksanrop, mycket bättre driftshistoria
Frågorna ett listningsanrop inte kan besvara
Namn och ordning är hela vad du får. Listningsanropen säger ingenting om synlighet, så dolda och mycket dolda blad anländer i listan och ser ut som vilket annat blad som helst. De rapporterar inga dimensioner för använt område, inga cellantal och inga dokumentegenskaper. Delen docProps/core.xml är också liten, men det finns ingen ren egenskapssondering idag, så metadata för författare och titel kostar fortfarande en fullständig Open. Det rena sättet att leva med det är att låta de billiga fakta routa varje fil och reservera de dyra för filer som överlever routningen. För filerna som fortsätter in i en djup läsning körs en skrivskyddad skanning av en stor .xls märkbart snabbare med _DisableGraphics := True, som hoppar över OfficeArt-tolkning. Spara bara aldrig från den instansen: ritlagret den hoppade över är borta ur modellen, och att spara skulle ta bort det från filen
Filer som klarar triage går vanligtvis vidare till djupare analys. Arbetsboksgranskningen och konverteringsverkstaden täcker räknarna per blad värda att samla in när en fullständig öppning är motiverad, och guiden om prestanda för stora arbetsböcker täcker hur man håller den fullständiga öppningen snabb
HotXLS är ett nativt Object Pascal-kalkylbibliotek för Delphi och C++Builder; hela API-ytan, inklusive inspektionsanropen som visas här, är dokumenterad på produktsidan för HotXLS Delphi Component