HotXLS BIFF8 XLUnicodeString dekoduoja pirmiausia perskaitydama cch ir fHigh vėliavą, tada pasirinkdama koduotę atitinkantį skaitytuvą: TXLSBlob.GetWideString su cch * 2 baitų skaičiumi, kai fHigh yra 1, ir TXLSBlob.GetString, kai fHigh yra 0. Suporuokite juos atvirkščiai ir įrašas grąžins tuščią arba perpus trumpesnę eilutę, bet niekada negrąžins išimties
Būtent todėl ši klaidų klasė brangiai kainuoja. Diagrama atsidaro, serijos nupiešiamos teisingai, ašys geros, o vienas trendline parašas tiesiog tuščias. Nieko žurnale, nieko išimčių tvarkyklėje, jokio sugadinto failo dialogo. Failas visą laiką buvo geras; skaitytuvas paprašė neteisingo baitų skaičiaus ir gavo tiksliai tai, ko paprašė
Kodėl BIFF8 eilutė grįžta tuščia?
BIFF8 eilutė grįžta tuščia todėl, kad ilgio saugiklis atmetė naudingąją apkrovą dar prieš skaitymą arba skaitytuvas sustojo ties pirmuoju rastu NUL. Abu keliai pagal konstrukciją yra tylūs. HotXLS sistemoje saugiklis paprastai yra aiški DataLength patikra įrašo tvarkyklėje, ir jį reikia skaičiuoti pagal koduotę: 16 bitų SXViewLink kūnui reikia 8 + cch * 2 baitų, o 8 bitų kūnui – tik 8 + cch. Pritaikykite plačių simbolių aritmetiką 8 bitų įrašui ir kiekvienas trumpas vardas nepraeis vartų. Antri spąstai yra NUL elgsena, nes TXLSBlob.GetString ir TXLSBlob.GetWideString abu ieško terminatoriaus iškoduotame rezultate ir ties juo nukerpa, o terminatoriui atsidūrus pirmoje pozicijoje grąžina tuščią eilutę. Perskaitykite 16 bitų kūną su puse baitų skaičiaus ir išlaikysite pirmus cch div 2 simbolių; perskaitykite 8 bitų kūną plačiu skaitytuvu ir baitų poros sudarys atsitiktinius kodų taškus. Garsiai pranešama tik apie per ilgą skaitymą: TXLSBlob.EnsureReadable kelia Blob read exceeds data size, kai prašymas išeina už blobo. Per trumpas skaitymas tokio signalo neturi
GetWideString skaičiuoja baitus, o ne simbolius
TXLSBlob.GetWideString(Index, Count) priima Count baitais. Viduje jis atlieka SetString virš PWideChar, naudodamas Count div SizeOf(WideChar), todėl perdavus simbolių skaičių eilutė tyliai sutrumpėja perpus. Tuo tarpu BIFF8 įrašų maketai eilutės ilgį išreiškia simboliais. Todėl kiekviena 16 bitų iškvietimo vieta turi pati turėti * 2 konversiją, o kiekviena 8 bitų vieta neturi jos turėti. Tai ta pati koduotės riba, kuri pasirodo tekstą rašant atgal, o ne skaitant, todėl verta ją perskaityti kartu su Unicode saugiu skaičiuoklių eksportu Delphi aplinkoje, jei jūsų konvejeris stumia eilutes abiem kryptimis
// 16 bitų XLUnicodeStringNoCch: cch simbolių, cch * 2 baitų
Name := Data.GetWideString(Start, cch * 2); // teisinga
Name := Data.GetWideString(Start, cch); // pusė teksto, be klaidos
// 8 bitų XLUnicodeStringNoCch: cch simbolių, cch baitų
Name := WideString(Data.GetString(Start, cch)); // teisinga
Name := Data.GetWideStringWithZero(Start, cch); // vis dar platus skaitytuvas
Ši konvencija galioja visur, kur baitų srautas apeinamas rankomis. Kai HotXLS iš Continue įrašų ($003C) vėl sujungia ilgą String įrašą ($0207, [MS-XLS] 2.4.268), plačioji šaka apskaičiuoja segCh iš segmento ilgio ir tada kviečia GetWideString(3, segCh * 2), nes įrašo kūnas prasideda 3 poslinkyje, o skaičius vis dar yra baitais. Rich-text skaitytuvas tą patį daro nuo 1 poslinkio pirmame Continue segmente. [MS-XLS] 2.5.293 garantuoja, kad kai fHighByte yra 1, lūžis eina ties dviejų baitų simbolio riba, todėl nereikia dalinio simbolio apskaitos, bet baitų aritmetiką vis tiek turite atlikti teisingai
Ką iš tikrųjų daro GetWideStringWithZero?
TXLSBlob.GetWideStringWithZero yra plačių simbolių skaitytuvas, išlaikantis įterptus NUL. Priesaga WithZero žymi NUL išlaikymą, o ne simbolio plotį: viduje jis atlieka tą patį SetString virš PWideChar, naudodamas Count div SizeOf(WideChar), kaip ir GetWideString, tik be terminatoriaus paieškos. Vieno baito atitikmuo yra TXLSBlob.GetStringWithZero, grąžinantis AnsiString. Varde niekur nepasakyta, kuris iš jų yra kuris, ir ši dviprasmybė kode realiai sukėlė klaidų. Verta įvardyti konkretų neteisingą skaitymą, nes jis atrodo labai įtikinamas: GetWideString reikia cch * 2, todėl GetWideStringWithZero turbūt priima cch tiesiogiai. Jis priima cch be skundo, grąžina WideString, o kompiliatorius patenkintas. Tačiau jis grąžina pusę simbolių, sudarytų iš neteisingų baitų porų. Teisingas 8 bitų kelias yra TXLSBlob.GetString su paprastu cch baitų skaičiumi, priskyrimo vietoje paverčiamas į WideString. HotXLS 2.376.0 ištaisė būtent šį netinkamą naudojimą dviejuose diagramos dekoderiuose
SXViewLink ir ilgio vartai pagal koduotę
SXViewLink ($0858, [MS-XLS] 2.4.316) yra aiškiausias pavyzdys, nes vienoje aštuonių baitų antraštėje sudeda abi asimetrijas. Maketas yra rt(2), unused(2), reserved(2), cch(1), fHigh(1), po jų eina XLUnicodeStringNoCch kūnas: fHigh = 1 reiškia cch * 2 UTF-16 baitų, fHigh = 0 reiškia cch vieno baito simbolių, o cch ribojamas 255, nes ilgio laukas yra vieno baito. HotXLS įrašo šį įrašą diagramos globals prieš Units, šalia PivotChartBits ($0859, [MS-XLS] 2.4.196), kai diagramos lapas susietas su PivotTable rodiniu; įrašų lygio šios mechanikos vaizdas pateiktas rašant BIFF8 PivotTable įrašus Delphi aplinkoje
// SXViewLink ([MS-XLS] 2.4.316): rt(2) unused(2) reserved(2) cch(1)
// toliau XLUnicodeStringNoCch - fHigh(1), po jo simboliai
PivCch := Item.FData.GetByte(6);
if (PivCch > 0) and (Item.FData.GetByte(7) <> 0) and
(Item.FData.DataLength >= LongWord(8 + PivCch * 2)) then
begin
Result.PivotSourceName := Item.FData.GetWideString(8, PivCch * 2);
Result.IsPivotChart := True;
end
else if (PivCch > 0) and (Item.FData.GetByte(7) = 0) and
(Item.FData.DataLength >= LongWord(8 + PivCch)) then
begin
Result.PivotSourceName := WideString(Item.FData.GetString(8, PivCch));
Result.IsPivotChart := True;
end;
Dvi šakos nėra dekoracija. Ankstesnė versija abi koduotes saugojo už plačiųjų simbolių išraiškos 8 + cch * 2, todėl Excel įrašytas 8 bitų rodinio vardas neperėjo saugiklio, o dekoderis grąžino tuščią PivotSourceName ir paliko IsPivotChart false. Pivot nuoroda dingo iš modelio be vienos diagnostikos. Tokia pati klaida buvo Trendline dekoderiuje ($2050, [MS-XLS] 2.4.328), kur vardo laukas eina po 28 baitų skaitinės naudingosios apkrovos, dviejų baitų cch yra 28 poslinkyje, fHigh – 30, o simboliai – 31; Excel 8 bitų simboliais įrašyti trendline parašai buvo iškoduojami kaip tuščios eilutės. Abu pataisyti tame pačiame leidime. 8 bitų atvejis nėra sena smulkmena, apsiribojanti Excel 2.0–4.0 failais: dabartinis Excel vis dar rašo 8 bitų BIFF8 naudingąsias apkrovas, kai kiekvienas simbolis telpa į vieną baitą
Kaip saugiai dekoduoti naują BIFF8 įrašą Delphi?
Kai laukas iš tikrųjų yra standartinis XLUnicodeString, naudokite TXLSBlob.GetBiffString, o ne rašykite šaką rankomis. Jis perskaito ilgio lauką, perskaito parinkties baitą, iškviečia atitinkamą skaitytuvą ir pastumia žymeklį už kūno. Du Boolean parametrai yra dalis, kurią reikia perskaityti atidžiai: is8bit nusako ilgio lauko plotį, o ne simbolių plotį, o iswide sako, ar po ilgio lauko apskritai yra fHigh parinkties baitas. BIFF versijos iki $0600 neturi nei vieno, nei kito
var
Offset: LongWord;
begin
Offset := 6; // SXViewLink cch baitas prasideda čia
// is8bit = ilgio laukas yra vieno baito pločio
// iswide = po ilgio lauko eina fHigh parinkties baitas
Name := Data.GetBiffString(Offset, True, True);
// Offset dabar rodo į pirmą baitą po eilutės kūno
Rankomis parašytos šakos vis dar vertingos, kai tvarkyklė turi atlaikyti nukirstą ar priešišką įvestį, nes GetBiffString remiasi tuo, kad EnsureReadable pakels išimtį, o ne jūsų valdoma ribų patikra. Todėl HotXLS diagramos dekoderis tikrina DataLength ir, užuot metęs išimtį, nieko negrąžina: sugadinta trečiosios šalies darbaknygė turi kainuoti vieną parašą, o ne visą dokumentą. Kompromisas sąmoningas ir būtent dėl jo koduotės saugiklis turi būti teisingas, nes jis blogą skaitymą paverčia tyla
Dar viena proceso detalė, išmokta tuo pačiu leidimu. Tvirtinkite išsaugotą ir vėl atvertą darbaknygę, o ne ką tik atmintyje sukurtą modelį. 2.376.0 partija taip pat atskleidė SXEx generatorių ([MS-XLS] 2.4.282), kuris deklaravo 24 baitų kūną, bet įrašė tik 22 ir išderino kiekvieną įrašą po PivotTable rodinio, įskaitant darbalapio EOF ir bet kurį po jo einantį diagramos lapo posrautį. Esami pivot testai to niekada nepastebėjo, nes visi tikrino atmintį. Eilučių dekodavimas turi tą pačią savybę: tik kelionė per failą pirmyn ir atgal iš tikrųjų patikrina baitų skaičius
Jei Delphi arba C++Builder dirbate su klasikiniais XLS vidiniais duomenimis ir nenorite prižiūrėti savo BIFF8 įrašų skaitytuvo, aukščiau aprašytos koduotės taisyklės jau įgyvendintos ir regresiškai patikrintos HotXLS Delphi skaičiuoklių komponente, kuris skaito ir rašo XLS bei XLSX be Excel ir OLE automatizavimo