Skupinsko normalizacijsko opravilo preglednic je tri problemi v enem plašču. Imate arhiv mešanih formatov: .xls iz ere BIFF, sodobni .xlsx, razpršeni .ods iz nekega eksperimenta z LibreOffice, in peščica datotek, ki jih nihče ne more odpreti, ker je geslo odneslo nekdanje zaposleni. Cilj je pretvoriti vse v XLSX in CSV. Različica tega opravila, ki jo večina napiše, je zanka, ki odpre vsako datoteko in jo shrani pod novo končnico, in deluje prav do takrat, ko nekdo vpraša, katere datoteke so izgubile grafikone, opustile makre ali sploh nikoli niso bile odprte. Zanka nima odgovora, ker sama pretvorba ne vodi nobene evidence. Delovni stol pa: najprej inventarizira, nato pretvori, tretjič preveri, in vse tri stopnje si morajo deliti informacije, da je katerakoli zanesljiva.
Sestavljanje tega delovnega stola v Delphiju ali C++Builderju pomeni spajanje štirih zmogljivosti HotXLS, od katerih nobena ne potrebuje nameščenega Excela kjer koli v cevovodu. Obstajata dve izvorni mašini, fasada BIFF8 za .xls in fasada OOXML za .xlsx in .ods. Obstajajo poceni sondirni klici, ki berejo metapodatke brez razčlenjevanja celotne datoteke. Obstajajo revizijski števci na list, ki vam povedo, kaj delovni zvezek dejansko vsebuje. In obstaja matrica pretvorb z dokumentiranim profilom zvestobe za vsako pot. Delo je v poznavanju, kje ima vsaka od teh oster rob, ker ga ima vsaka, in ti robovi so točno tisto, kar čiste nočne serije spremeni v ponedeljkovo zjutraj incident.
Sondirajte pred nalaganjem: imena listov in zaznavanje šifriranja
Odpiranje 200 MB delovnega zvezka samo zato, da bi ugotovili, da je šifriran, zapravi minute na datoteko, in pomnoži čez velik arhiv zapravi dni. Obe fasadi izpostavljata GetSheetNames, ki bere metapodatke listov brez poseljenja delovnega zvezka. Implementacija BIFF pregleduje samo zapise BoundSheet na začetku toka; implementacija OOXML bere samo workbook.xml znotraj zipa. Ob njej CanReadEncrypted zazna vsebnik šifriranja brez poskusa dešifriranja:
var
Probe: TXLSXWorkbook;
Names: TStringList;
begin
Names := TStringList.Create;
Probe := TXLSXWorkbook.Create;
try
if Probe.CanReadEncrypted(FileName) then
begin
Writeln(FileName + ': encrypted container - route to manual handling');
Exit;
end;
if Probe.GetSheetNames(FileName, Names) <= 0 then
Writeln(FileName + ': unreadable - quarantine')
else
Writeln(Format('%s: %d sheet(s), first "%s"',
[FileName, Names.Count, Names[0]]));
finally
Probe.Free;
Names.Free;
end;
end;
Dve operativni podrobnosti naredita to zanko poceni. GetSheetNames ne ponastavi niti ne poseli primerka delovnega zvezka, zato en sondirni objekt lahko razvrsti tisoče datotek brez ponovnega ustvarjanja. In različica klica na fasadi XLS razume tudi pakete .xlsx, kar jo naredi za priročno enojno sondo, ko razširitve datotek ne moremo zaupati, kot v arhivu te starosti pogosto ne moremo. Triažiranje pred nalaganjem si zasluži lastno obravnavo; mehanika lahke preiskave je v našem članku o navajanju listov in lahkem pregledu delovnih zvezkov.
Štetje tega, kar delovni zvezek resnično vsebuje
Ko datoteka prestane triaž, revizijski prehod odloči o njeni poti pretvorbe. Fasada XLSX izpostavlja števec za vsako skupino funkcij, ki vpliva na odločitev o zvestobi: spojene celice, grafikoni, slike, pogojni formati, veljavnosti podatkov, tabele, hiperpovezave in komentarji, plus zastavice na ravni delovnega zvezka za makre, zaščito in izvorni format. Pot pretvorbe za datoteko je skoraj v celoti odvisna od tega, kateri od teh pridejo nazaj nični.
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
I: Integer;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open(FileName) <> 1 then Exit;
for I := 0 to Book.Sheets.Count - 1 do
begin
Sheet := Book.Sheets[I];
Writeln(Format('%s: cells=%d merges=%d charts=%d cf=%d dv=%d protected=%s',
[Sheet.Name, Sheet.Cells.Count, Sheet.MergedCells.Count,
Sheet.Charts.Count, Sheet.ConditionalFormats.Count,
Sheet.DataValidations.Count, BoolToStr(Sheet.IsProtected, True)]));
end;
if Book.HasVbaProject then
Writeln(' contains VBA project - macro policy applies');
if Book.ExternalLinks.Count > 0 then
Writeln(Format(' %d external link(s)', [Book.ExternalLinks.Count]));
finally
Book.Free;
end;
end;
Cells.Count preberite z eno pridržkom v mislih. Shramba celic je redka, zato število šteje instancirane celice in ne pravokotnega območja uporabljenega obsega. List z eno vrednostjo v A1 in drugo v ZZ9999 poroča dve celici in ne milijon ali tako, ki ležijo med njima. Enakovredna preiskava na strani BIFF uporablja meje UsedRange skupaj s ForEachCell, in nosi pomik za eno, ki zapelje skoraj vsakogar prvič: UsedRange.FirstRow in sorodniki so z osnovo 0, medtem ko je Cells.Item[Row, Col] z osnovo 1. Prehod, ki pozabi dodati ena vsaki meji, revidira napačen pravokotnik in tega nikoli ne pove.
Dve polgi znižata strošek revizijskega prehoda brez pisanja nad velikimi zastarelimi datotekami. Nastavitev _DisableGraphics na true pred odpiranjem .xls v celoti preskoči razčlenjevanje risalne plasti OfficeArt, kar prihrani resnični čas na delovnih zvezkih, gostih z oblikami. Toda to je strogo samo-bralna optimizacija: shranjevanje iz primerka, odprtega na ta način, bi odvrgle risbe, ki jih ni nikoli razčlenil, zato zastavica sodi samo na poti, ki datoteke nikoli ne bodo zapisale nazaj. Ko revizija potrebuje vsebino posameznih celic in ne le števil, povratni klic ForEachCell neposredno hodi po poseljenih celicah in obide strošek Variant na dostop, ki ga plačujejo indeksirne lastnosti celic ob vsakem branju, kar se hitro sešteje pri milijonih celic.
Zgodaj normalizirajte nedosledne kode vrnjenih vrednosti
Klici HotXLS I/O poročajo napake prek celih rezultatov in ne prek izjem, in konvencije niso enotne čez API. Večina klicev odpiranja in shranjevanja vrne 1 pri uspehu in -1 pri neuspehu. GetSheetNames vrne število listov ali -1 z izpraznjenim seznamom. XLSX SaveAsHTML znova prelomi vzorec in vrne 0 za uspeh, -1 za indeks lista izven obsega. Delovni stol, ki preverja = 1 povsod, bo tiho napačno razvrščal klice, ki signalizirajo uspeh na drug način, in tisti, ki preverja <> -1, bo pogoltnil tiste, ki spodletijo z drugačno kodo.
Pravilo, ki preživi stik s celotnim API-jem, je ožje kot izgleda: za klice, ki vračajo število, obravnavajte <= 0 kot neuspeh, preverite dokumentirano vrednost uspeha za vsako rutino shranjevanja, ki jo dejansko uporabljate, in oboje postavite za eno majhno funkcijo preverjanja rezultatov, da konvencija živi na točno enem mestu. Paketni cevovodi pogosteje spodletijo od počasnega kopičenja nepreverjenih kod vrnjenih vrednosti kot od kakšne eksotične hrošče razčlenjevalnika, in cena napake pri tem je štirideset tisoč datotek pozneje, ko si nihče ne zapomni, katere pretvorbe so se dejansko izvedle.
Matrica pretvorb in kje vsaka pot izgubi podatke
Obe fasadi si delita konverzijsko delo. TXLSXWorkbook odpira XLSX, ODS in CSV, ter shranjuje XLSX, ODS, CSV, HTML, RTF in AES-šifrirani XLSX. TXLSWorkbook odpira in shranjuje BIFF, ter izvozi HTML, RTF in CSV. Koristno je, da vsaka pot prihaja z dokumentiranim profilom zvestobe in ne nejasno obljubo pravilnosti, zato se lahko vnaprej odločite, katere poti so varne za katere datoteke.
Izvoz CSV piše UTF-8 z BOM, CRLF konci vrstic in citiranje RFC 4180. Kar ne počne, je ovrednotiti formule: celica z =SUM(...) se izvozi kot dobesedno besedilo formule, zato se list formul, ne da bi vrednosti izračunali prej, izlije kot list nizov. Izvoz HTML proizvede eno samo tabelo, s colspan in rowspan kot nadomestki za spojene celice in z osnovno vrstičnim slogom. Izvoz RTF ima ostrejšo omejitev: ne more premoščati spojenih celic čez stolpce, zato nadaljevalnih celic spojitve pridejo prazne. Uvoz ODS je po nameri lahek, kakor dokumentira sama knjižnica. Skalarne vrednosti in predpomnjeni rezultati formul prispejo; slogi, živi izrazi formul ODF in risbe pa ne. To je pomembno, ko arhiv vsebuje resnične datoteke OpenDocument, ki jih ureja OASIS ODF 1.3, kjer koli blizu vizualno zveste pretvorbe potrebuje več, kot je ta pot uvoza bila zasnovana prenesti, in revizijski prehod je tisto, kar vam pove, da te datoteke obstajajo, preden jih serija tiho splošči.
SaveXLSWorkbookAsXLSX je podatkovni most, ne postavitveni most
Fasada BIFF ne more neposredno pisati OOXML, zato pot iz .xls v .xlsx teče skozi funkcijo SaveXLSWorkbookAsXLSX v enoti lxXlsxExport. Zvestoba tega mostu je vredna jasne navedbe, ker ime nakazuje več, kot ga je. Kopira vrednosti, formule, formate številk, barve zapolnitev, jedrne atribute pisave, širine stolpcev in nastavitve pogleda, kot so mrežne črte. Ne kopira obrob, spojenih obsegov, komentarjev, grafikonov ali pogojnih formatov. Za normalizacijo kakovosti podatkov, kjer bo sistem dolvodni razčlenil rezultat in nihče ne gleda na oblikovanje, je to točno dovolj in nič ni izgubljenega, kar kdo potrebuje. Za oblikovano poročilo, namenjeno branju osebe, ni dovolj, in to je točno tisto, kjer revizijski števci zaslužijo svoje mesto: datoteka, ki jo je revizija označila kot vsebujočo grafikone in pogojne formate, bi morala iti v ročno vrsto in ne skozi most, ki bo oboje opustil brez besede.
var
Legacy: IXLSWorkbook; // interface reference: do not Free
Modern: TXLSXWorkbook;
begin
if SameText(ExtractFileExt(FileName), '.xls') then
begin
Legacy := TXLSWorkbook.Create;
if Legacy.Open(FileName) <= 0 then Exit;
if SaveXLSWorkbookAsXLSX(Legacy,
ChangeFileExt(FileName, '.xlsx')) <= 0 then
Writeln('bridge failed: ' + FileName);
end
else
begin
Modern := TXLSXWorkbook.Create;
try
Modern.StreamingWrite := True; // stream sheet XML into the zip
if Modern.Open(FileName) = 1 then
Modern.SaveAsCSV(ChangeFileExt(FileName, '.csv'), 0, ',');
finally
Modern.Free;
end;
end;
end;
Zgornja zanka prikazuje tudi vzorec pretočnega izpisa na strani OOXML. Nastavitev StreamingWrite na true XML delovnega lista neposredno pretaka v izhodni paket namesto, da bi ga uprizarjal kot en ogromen niz v pomnilniku, kar je razlika med udobnim zaganjanjem in zrušitvijo po izčrpanju pomnilnika, ko datoteke dosežejo stotisoče vrstic. Dimenzioniranje in pomnilniško obnašanje tega načina sta posebej obravnavana v našem članku o pretočnih pisanjih za paketna opravila na strežniku. Ena lastnost je še pomembna za serijo, ki želi izkoristiti vsako jedro: nobena fasada ni varna za niti, toda nobena si ne deli globalnega stanja, zato je podprti vzorec za vzporedno pretvorbo en primerek delovnega zvezka na delavsko nit, brez zaklepanja med njimi.
Gesla zaprte datoteke in kaj z njimi
Zaklenjene datoteke arhiva se delijo jasno po formatu, in delitev odloča, kam gredo. Zastarelo šifriranje .xls, bodisi RC4, RC4 prek CryptoAPI ali stara XOR zamedlitev, je berljivo: geslo posredujte Open in datoteka se pretvori kot katera koli druga. Šifriran paket .xlsx je druga zgodba. HotXLS jih zazna z CanReadEncrypted, toda ne more jih dešifrirati, zato je edina poštena poteza usmerjanje v vrsto, kjer vsako od njih odpre in znova shrani v Excelu človeška roka, preden se znova vključi v cevovod. Ta asimetrija je vredna načrtovanja vnaprej, ker so šifrirani XLSX datoteke najverjetnejše tiste, za katere se nekdo resnično zanima.
Zaključevanje zanke s preverjanjem
Tretja stopnja je tista, ki se jo preskoči, in preskakovanje je tisto, kar skupinsko pretvorbo obrne v odgovornost. Nobena pot shranjevanja v HotXLS ne ovrednoti formul. Excel ob odprtju datoteke znova izračuna, zato pretvorba XLSX v XLSX ostane pravilna, toda cilj CSV dobesedno prejme besedilo formule, razen če cevovod najprej zažene Calculate na celicah in rezultate zapiše nazaj. Vedeti to vnaprej je razlika med CSV-jem polnim številk in CSV-jem polnim nizov =SUM(...), ki jih nihče ne opazi, dokler uvoz dolvodni ne zatakne nanje.
Samo preverjanje je dovolj poceni, da za njegovo opustitev ni opravičila. Vsako pretvorjeno datoteko znova odprite z isto knjižnico, znova zaženite revizijske števce in jih primerjajte z vrednostmi pred pretvorbo, ki jih je inventarni prehod že zabeležil. Število listov, ki je padlo, število grafikonov, ki je šlo na nič, kjer je vir imel tri, število celic, ki je strmoglavilo: vsako je tiha izguba, ujeta za ceno drugega odpiranja. Spot-preverite vzorec vizualno v Excelu ali LibreOffice poleg tega, in kombinacija ujame ogromno večino škode pretvorbe, preden jo odpošljete. To je celoten razlog, zakaj inventarna stopnja hrani vrednosti za stopnjo preverjanja. Brez vrednosti pred njimi vrednosti po njej ne dokazujejo ničesar.
Delovni stol revizija-najprej skupinsko pretvorbo spremeni v merljiv postopek s karantenskim pasom za datoteke, ki ne morejo prestati čisto. Vsi klici sondiranja, štetja in pretvorbe, prikazani tukaj, so del HotXLS Component, ki jih izvaja izvorno v-procesu brez avtomatizacije Excela.