Skaičiuoklėje yra klientų vardų stulpelis. Kai kurie jų – kinų, kiti – kirilicos rašmenimis, keli turi vokiškus umliautus ar prancūziškus akcentus. Eksportuojate jį į CSV, atidarote rezultatą ir kiekvienas simbolis lieka nepakitęs. Tą pačią darbo knygą eksportuojate į RTF, kad sukurtumėte serijinio laiškų siuntimo šabloną, atidarote teksto rengyklėje ir matote, kad ne ASCII vardai pavirto į klaustukų eiles. Duomenys niekada nesikeitė. Pasikeitė jūsų įrašyto formato koduotės sutartis, ir kiekvienas eksporto kelias turi vis kitokią
Tai yra spąstai, į kuriuos patenka biblioteka, kuri iš pažiūros atrodo visiškai palaikanti „Unicode“. Langelio tekstas viduje saugomas kaip WideString, todėl modelis niekada nepraranda nė vieno simbolio. Praradimas įvyksta ties riba – rašyklėje, kuri turi serializuoti tą tekstą į formatą su savais nuostatais, kurie baitai yra leistini ir kaip turi būti koduojamas bet kas už leistino diapazono ribų. Sutvarkius vieną rašyklę, vis tiek galima išleisti kitą, kuri darko tą patį tekstą. Pataisymas nėra visuotinis jungiklis. Tai yra atskiras, teisingas sprendimas kiekviename kelyje
RTF iš prigimties yra saugus 7 bitų formatas
„Rich Text Format“ atsirado anksčiau nei „Unicode“ ir buvo sukurtas taip, kad išliktų perduodant per sistemas, praleidžiančias tik spausdinamą ASCII. RTF dokumento antraštėje deklaruojamas kodų puslapis, o bet koks simbolis, kurio rašyklė negali atvaizduoti tame kodų puslapyje, turi būti išvestas kaip kaitos seka (escape), o ne kaip grynasis baitas. Atitinkama kaitos seka yra \u, kuri perneša koduotą 16 bitų kodo vienetą, po kurio seka atsarginis ASCII simbolis, skirtas skaitytuvams, per seniems, kad išvis suprastų šią seką
„HotXLS“ taip įrašo RTF. Dokumento antraštė pradedama deklaruojant kodų puslapį tokiu formatu kaip \ansi\ansicpg1252\uc1, o lxRTF modulyje esanti rašyklė pereina per kiekvieną eilutę, išvesdama bet kokį simbolį, esantį už paprasto ASCII ribų, kaip \u kaitos seką, kad baitų srautas išliktų švarus 7 bitų lygmenyje, nepriklausomai nuo to, ką gali talpinti deklaruotas kodų puslapis. Toks kodo taškas kaip U+4E2D tampa tiesiogine seka \u20013?, o ne grynuoju baitu, kurį peržiūros programa vėliau bandytų interpretuoti naudodama bet kokį numanomą kodų puslapį. Be šios drausmės bet kas, kas nepatenka į deklaruotą kodų puslapį, neturi legalaus baitinio atvaizdavimo, o rašyklė, išvedanti grynąją reikšmę, sukuria klaustukus, nuo kurių ir prasidėjo šis straipsnis
Svarbu atsiminti, kad deklaruotas kodų puslapis ir kaitos sekos yra dvi vienos sutarties pusės. Vien tik kodų puslapio deklaravimas nepadeda tekstui, kuris į jį nepatenka. Kaitos sekų išvedimas be deklaruoto kodų puslapio palieka atsarginius simbolius dviprasmiškais. Abu turi būti teisingi kartu, todėl rašyklė, apdorojanti tik vieną iš jų, vis tiek susiduria su problemomis atidarant pirmąją daugiakalbę darbo knygą
HTML kaitos sekos reiškia daugiau nei tik kampinius skliaustelius
HTML eksportas sukuria kelių lakštų dokumentą, kurio navigacijos rėmeliuose lakštų pavadinimai rodomi kaip matomas tekstas. Šie pavadinimai yra autoriaus valdomos eilutės, kuriose gali būti bet kokių simbolių, įskaitant ir turinčius reikšmę žymėjimui. Lakštas, kurio pavadinimas yra Q1 & Q2 <draft>, į puslapį turi patekti kaip koduoti objektai (escaped entities), kitaip kampiniai skliausteliai atidarys netikrą žymą, o ampersandas pradės objekto nuorodą, kuri niekada nebuvo numatyta. Tai yra įprastas HTML kodavimas, o jo praleidimas rėmelio etiketėje yra tokia klaida, kuri sėkmingai pereina kiekvieną testą, sudarytą vien iš ASCII lakštų pavadinimų
Koduotės klausimas yra vienu sluoksniu giliau. Kai ne ASCII simboliai patenka į kontekstą, kuriam negarantuojamas UTF-8 pateikimas, saugus atvaizdavimas yra skaitmeninė simbolio nuoroda, todėl U+00E9 užrašomas kaip é, o ne kaip grynasis baitas, kurio reikšmė priklauso nuo atsako simbolių rinkinio. Veidrodinis šios taisyklės atspindys taikomas ir duomenų įvedimui. Darbo knygoje, nuskaitytoje iš XLSX, yra bendrinamų eilučių, kuriose simbolis jau gali būti išsaugotas kaip skaitmeninis XML objektas, ir tas objektas turi būti iškoduotas į vieną sveiką simbolį, prieš jam patenkant į langelių modelį. Iškodavus jį nerūpestingai, padalijant kodo tašką į atskirus baitus, vienas simbolis vėl atsiranda kaip du „mojibake“ (iškraipyto teksto) gabaliukai, kurių joks vėlesnis eksportas nebegalės ištaisyti
XLSX konteineris yra ZIP failas, o ZIP turi savo pavadinimų koduotę
XLSX failas yra ZIP archyvas, kuriame saugomas kiekvieno jo elemento pavadinimas. ZIP formatas yra pakankamai senas, todėl pradinėje jo specifikacijoje nebuvo nieko užsimenama apie tų pavadinimų koduotę, taigi skaitytuvas, neradęs jokio signalo, priskiria archyvui vietinį kodų puslapį. Ši prielaida tampa klaidinga, kai elemento pavadinime atsiranda ne ASCII simbolis – tai nutinka su lokalizuotais darbalapio dalių pavadinimais ir su įterpta medija, kurios failų pavadinimuose yra akcentų ar ne lotyniškų rašmenų
Pataisymas tėra vienas bitas. Bendrosios paskirties 11-asis bitas kiekvieno vietinio failo antraštėje deklaruoja, kad elemento pavadinimas koduojamas kaip UTF-8. „HotXLS“ tikrina būtent šį bitą skaitydama archyvą, testuodama bendrosios paskirties vėliavėles pagal kaukę $0800, o skaitytuvas arba rašyklė, ignoruojanti tai, neteisingai perskaitys pavadinimą, kurį teisinga realizacija išsaugojo kaip UTF-8. Šį bitą nustatyti ir jo paisyti nieko nekainuoja, ir tai yra skirtumas tarp elemento pavadinimo, kuris išlieka po viso proceso, ir to, kuris atkeliauja sugadintas dar prieš pradedant analizuoti skaičiuoklės turinį
Registro keitimas ir skaičių nuskaitymas slepia tą patį pavojų
Formulių vertinimas yra ta sritis, kurioje „Unicode“ saugumas tampa nebe serializacijos, o palyginimo klausimu. Funkcija SEARCH nereaguoja į raidžių registrą, o tai reiškia, kad prieš ieškant poeilutės, ji turi suvienodinti registrą. Neteisingas būdas tai padaryti – per ANSI kodų puslapį, nes tokiu būdu keičiant ne ASCII teksto registrą į didžiąsias raides, simboliai nukreipiami per siaurą kodų puslapį ir sugadinama viskas, kas peržengia jo ribas. Teisingas būdas yra plačiųjų eilučių (wide-string) vertimas didžiosiomis raidėmis, išlaikantis visą UTF-16 diapazoną. Būtent dėl šios priežasties „HotXLS“ keičia registrą naudodamas WideUpperCase, kad ieškant akcentuoto ar ne lotyniško teksto būtų randami tie patys pateikti simboliai, o ne kodų puslapio iškraipytas jų apytikslis atitikmuo
Formulių leksemų analizatorius (tokenizer) turi susijusį įpareigojimą, kuris neturi nieko bendro su raidėmis, bet tiesiogiai susijęs su tuo, kur baigiasi leksema. Mokslinė notacija, tokia kaip 1E3 arba 2.5E-3, yra vienas skaitinis literalas, todėl skaitytuvas turi atpažinti E, pasirenkamą ženklą ir po jo sekančius skaitmenis kaip skaičiaus dalį, o ne padalinti įvestį į pavadinimą, po kurio eina atskiras skaičius. Skaitytuvas, neteisingai apdorojantis šią situaciją, paverčia visiškai galiojančią konstantą sintaksės analizės klaida arba, dar blogiau, tyliai klaidinga išraiška. Tai priklauso tai pačiai diskusijai, nes abiem atvejais kalbama apie tai, ar skaitytuvas priima teisingą sprendimą simbolių lygmeniu: vienas susijęs su tuo, kaip pakeisti simbolio registrą palyginimui, kitas – ar simbolis tęsia dabartinę leksemą
Daugiakalbės darbo knygos kūrimas ir eksportavimas
Viešoji API nereikalauja jūsų galvoti apie jokius šiuos dalykus. Jūs sukuriate darbo knygą iš WideString langelių reikšmių ir iškviečiate norimą eksporto įėjimo tašką. Sprendimai dėl koduotės priimami kiekvienos rašyklės viduje. Toliau pateiktame pavyzdyje lakštas užpildomas tekstu keliais rašmenimis, tada iš tos pačios darbo knygos įrašomas ir RTF, ir HTML failas, todėl abu keliai veikia su identiška įvestimi
uses
lxHandle;
procedure ExportMultilingualWorkbook;
var
Book: IXLSWorkbook;
Sheet: IXLSWorksheet;
begin
Book := TXLSWorkbook.Create;
try
Sheet := Book.Sheets.Add('Customers');
Sheet.Cells[1, 1].Value := 'Name';
Sheet.Cells[1, 2].Value := 'City';
// Cell text is held as WideString, so every script survives the model.
Sheet.Cells[2, 1].Value := '王伟'; // Chinese
Sheet.Cells[2, 2].Value := '北京';
Sheet.Cells[3, 1].Value := 'Müller'; // German umlaut
Sheet.Cells[3, 2].Value := 'Köln';
Sheet.Cells[4, 1].Value := 'Иванов'; // Cyrillic
Sheet.Cells[4, 2].Value := 'Москва';
Sheet.Cells[5, 1].Value := 'Désirée'; // French accents
Sheet.Cells[5, 2].Value := 'Montréal';
// RTF: the lxRTF writer declares the code page and emits every
// non-ASCII character as a \u escape, keeping the file 7-bit clean.
Book.SaveAsRTF('Customers.rtf');
// HTML: sheet names are HTML-escaped and non-ASCII text is written
// so it does not depend on a guessed response charset.
Book.SaveAsHTML('Customers.html');
finally
Book := nil;
end;
end;
Abu iškvietimai grąžina Integer būseną, ir abu naudoja tą patį atmintyje esantį tekstą. Kviečiančiame kode niekas nedeklaruoja kodų puslapio ar neperkoduoja simbolio, nes atsakomybė tenka rašyklei, kuri išmano savo formatą. Darbo knygos lygio SaveAsCSV veikia pagal tą patį principą, jei jums reikia eksportuoti failą su skirtukais iš to paties šaltinio
// Same workbook, a third export path with its own encoding rules.
Book.SaveAsCSV('Customers.csv');
„Unicode“ saugumas priklauso nuo kelio, o ne nuo bibliotekos
Pamoka, kurią verta įsiminti, yra ta, kad nėra vienos vietos, kurioje būtų užtikrintas „Unicode“ saugumas. RTF formatui reikalingas deklaruotas kodų puslapis ir \u kaitos sekos. HTML reikalauja objektų kodavimo žymėjimui svarbiems simboliams ir skaitmeninių nuorodų ten, kur negarantuojamas simbolių rinkinys, taip pat teisingo objektų, atkeliaujančių bendrinamose eilutėse, iškodavimo. ZIP konteineriui reikia nustatyti bendrosios paskirties 11-ąjį bitą, kad UTF-8 elemento pavadinimas būtų skaitomas kaip UTF-8. Formulių vertinimui reikia plačiųjų eilučių (wide-string) registro suvienodinimo ir leksemų analizatoriaus, kuris išlaiko mokslinę notaciją vientisą. Kiekviena iš šių sutarčių yra skirtinga, ir biblioteka gali atitikti vieną, tuo pačiu tyliai pažeisdama kitą. Būtent dėl šios priežasties įrankis, kuris teisingai apdoroja CSV, vis tiek gali pateikti RTF failą, pilną klaustukų
Jei jūsų eksportas remiasi formatais su skirtukais, kompromisai tarp jų aptariami mūsų CSV, TSV ir HTML eksporto apžvalgoje, o kai šaltinis yra rezultatų rinkinys, o ne rankiniu būdu sukurtas lakštas, šablonai, aprašyti duomenų bazių eksportavime „Delphi“ ataskaitoms, natūraliai dera su čia aprašytomis kodavimo taisyklėmis. Visa tai tiekiama kaip „Delphi“ ir „C++Builder“ skirto HotXLS komponento dalis, kartu su skaitymo, formulių ir formatavimo API, kurios aprašomos kitur šiame tinklaraštyje