Ak ste vývojár a stojíte pred úlohou vybrať kódovanie, Unicode bude takmer vždy tým správnym riešením. Konkrétna metóda reprezentácie závisí od kontextu, ale aj tu najčastejšie existuje univerzálna odpoveď - UTF-8. Dobrá vec na tom je, že vám umožňuje používať všetky znaky Unicode bez výdavkov príliš veľa vo väčšine prípadov veľa bajtov. Je pravda, že pre jazyky, ktoré používajú viac ako len latinskú abecedu, je prinajmenšom „nie príliš veľa“. dva bajty na znak. Môžeme to urobiť lepšie bez návratu k prehistorickým kódovaniam, ktoré nás obmedzujú len na 256 dostupných znakov?
Nižšie navrhujem zoznámiť sa s mojím pokusom odpovedať na túto otázku a implementovať relatívne jednoduchý algoritmus, ktorý vám umožňuje ukladať riadky vo väčšine jazykov sveta bez pridania redundancie, ktorá je v UTF-8.
Vylúčenie zodpovednosti. Okamžite urobím niekoľko dôležitých výhrad: popísané riešenie nie je ponúkané ako univerzálna náhrada za UTF-8, je vhodný iba v úzkom zozname prípadov (viac o nich nižšie) a v žiadnom prípade by sa nemal používať na interakciu s API tretích strán (ktoré o tom ani nevedia). Na kompaktné ukladanie veľkých objemov textových údajov sú najčastejšie vhodné univerzálne kompresné algoritmy (napríklad deflate). Navyše, už v procese vytvárania môjho riešenia som našiel existujúci štandard v samotnom Unicode, ktorý rieši rovnaký problém - je o niečo komplikovanejší (a často horší), ale stále je to akceptovaný štandard, a nie len položený spolu na kolene. Poviem vám aj o ňom.
O Unicode a UTF-8
Na začiatok pár slov o tom, čo to je unicode и UTF-8.
Ako viete, 8-bitové kódovanie bolo populárne. S nimi bolo všetko jednoduché: 256 znakov môže byť očíslovaných číslami od 0 do 255 a čísla od 0 do 255 môžu byť samozrejme reprezentované ako jeden bajt. Ak sa vrátime na úplný začiatok, kódovanie ASCII je úplne obmedzené na 7 bitov, takže najvýznamnejší bit v jeho bajtovej reprezentácii je nula a väčšina 8-bitových kódovaní je s ním kompatibilná (líšia sa iba „horným“ časť, kde najvýznamnejší bit je jedna ).
Ako sa Unicode líši od týchto kódovaní a prečo je s ním spojených toľko špecifických zobrazení - UTF-8, UTF-16 (BE a LE), UTF-32? Urobme si to po poriadku.
Základný štandard Unicode popisuje iba súlad medzi znakmi (av niektorých prípadoch aj jednotlivými zložkami znakov) a ich číslami. A v tomto štandarde je veľa možných čísel - od 0x00
na 0x10FFFF
(1 114 112 kusov). Ak by sme chceli do premennej vložiť číslo v takomto rozsahu, nestačil by nám ani 1 ani 2 bajty. A keďže naše procesory nie sú príliš navrhnuté na prácu s trojbajtovými číslami, boli by sme nútení použiť aj 4 bajty na znak! Toto je UTF-32, ale práve pre túto „plytvanie“ nie je tento formát populárny.
Našťastie poradie znakov v rámci Unicode nie je náhodné. Celá ich zostava je rozdelená na 17"lietadlá“, z ktorých každá obsahuje 65536 (0x10000
) «kódové body" Koncept „kódového bodu“ je tu jednoduchý číslo znaku, ktoré mu pridelil Unicode. Ale, ako je uvedené vyššie, v Unicode nie sú očíslované len jednotlivé znaky, ale aj ich komponenty a servisné značky (a niekedy vôbec nič nezodpovedá číslu - možno zatiaľ, ale pre nás to nie je také dôležité), takže správnejšie je vždy hovoriť konkrétne o počte samotných čísel a nie o symboloch. V nasledujúcom texte však kvôli stručnosti budem často používať slovo „symbol“, čo znamená pojem „bod kódu“.
Unicode lietadlá. Ako vidíte, väčšina z toho (lietadlá 4 až 13) je stále nevyužitá.
Najpozoruhodnejšie je, že všetka hlavná „buničina“ leží v nulovej rovine, nazýva sa „Základná viacjazyčná rovina". Ak riadok obsahuje text v jednom z moderných jazykov (vrátane čínštiny), neprekročíte túto rovinu. Nemôžete však odrezať ani zvyšok Unicode - napríklad emotikony sa nachádzajú hlavne na konci ďalšie lietadlo,"Doplnková viacjazyčná rovina"(vychádza z 0x10000
na 0x1FFFF
). Takže UTF-16 robí toto: všetky znaky spadajúce do neho Základná viacjazyčná rovina, sú zakódované „tak ako sú“ so zodpovedajúcim dvojbajtovým číslom. Niektoré čísla v tomto rozsahu však vôbec neoznačujú konkrétne znaky, ale naznačujú, že po tomto páre bajtov musíme zvážiť ďalší - spojením hodnôt týchto štyroch bajtov dohromady dostaneme číslo, ktoré pokrýva celý platný rozsah Unicode. Táto myšlienka sa nazýva „náhradné páry“ – možno ste o nich už počuli.
Takže UTF-16 vyžaduje dva alebo (vo veľmi zriedkavých prípadoch) štyri bajty na "bod kódu". Je to lepšie ako neustále používať štyri bajty, ale latinka (a ďalšie znaky ASCII) pri kódovaní týmto spôsobom stráca polovicu miesta na nuly. UTF-8 je navrhnutý tak, aby to napravil: ASCII v ňom zaberá, ako predtým, iba jeden bajt; kódy z 0x80
na 0x7FF
- dva bajty; od 0x800
na 0xFFFF
- tri a od 0x10000
na 0x10FFFF
- štyri. Na jednej strane sa latinská abeceda stala dobrou: kompatibilita s ASCII sa vrátila a distribúcia je rovnomernejšie „rozložená“ od 1 do 4 bajtov. Ale iné abecedy ako latinka, bohužiaľ, v porovnaní s UTF-16 nijako neprospievajú a mnohé teraz vyžadujú tri bajty namiesto dvoch – rozsah pokrytý dvojbajtovým záznamom sa zúžil 32-krát, pričom 0xFFFF
na 0x7FF
a nie je v ňom zahrnutá ani čínština, ani napríklad gruzínčina. Cyrilika a päť ďalších abecied - hurá - šťastie, 2 bajty na znak.
Prečo sa to deje? Pozrime sa, ako UTF-8 predstavuje kódy znakov:
Priamo na znázornenie čísel sa tu používajú bity označené symbolom x
. Je vidieť, že v dvojbajtovom zázname je len 11 takýchto bitov (zo 16). Vodiace bity tu majú len pomocnú funkciu. V prípade štvorbajtového záznamu je pre číslo kódového bodu alokovaných 21 z 32 bitov - zdalo by sa, že tri bajty (ktoré spolu dávajú 24 bitov) by stačili, no servisné značky zaberajú príliš veľa.
Je to zlé? Nie naozaj. Na jednej strane, ak nám veľmi záleží na priestore, máme kompresné algoritmy, ktoré dokážu jednoducho eliminovať všetku extra entropiu a redundanciu. Na druhej strane, cieľom Unicode bolo poskytnúť čo najuniverzálnejšie kódovanie. Napríklad riadok zakódovaný v UTF-8 môžeme zveriť kódu, ktorý predtým fungoval iba s ASCII, a nebáť sa, že uvidí znak z rozsahu ASCII, ktorý tam v skutočnosti nie je (napokon, v UTF-8 všetky bajty začínajúce od nulového bitu - to je presne to, čo je ASCII). A ak zrazu chceme odrezať malý chvost z veľkého reťazca bez toho, aby sme ho dekódovali od úplného začiatku (alebo obnoviť časť informácií po poškodenom úseku), ľahko nájdeme odsadenie, kde znak začína (stačí na preskočenie bajtov, ktoré majú bitovú predponu 10
).
Prečo potom vymýšľať niečo nové?
Zároveň sa občas vyskytnú situácie, keď sú kompresné algoritmy ako deflate zle použiteľné, ale chcete dosiahnuť kompaktné uloženie reťazcov. Osobne som sa pri uvažovaní o stavbe stretol s týmto problémom
Samostatne by som chcel poznamenať ešte jednu nepríjemnú nuanciu, ktorá vzniká pri použití UTF-8 v takejto dátovej štruktúre. Vyššie uvedený obrázok ukazuje, že keď je znak zapísaný ako dva bajty, bity súvisiace s jeho číslom nie sú za sebou, ale sú oddelené párom bitov 10
v strede: 110xxxxx 10xxxxxx
. Z tohto dôvodu, keď spodných 6 bitov druhého bajtu pretečie v kóde znakov (t.j. dôjde k prechodu 10111111
→ 10000000
), potom sa zmení aj prvý bajt. Ukazuje sa, že písmeno „p“ je označené bajtmi 0xD0 0xBF
a ďalšie „r“ už je 0xD1 0x80
. V strome prefixov to vedie k rozdeleniu nadradeného uzla na dva - jeden pre predponu 0xD0
, a ďalší pre 0xD1
(aj keď celá azbuka sa dala zakódovať iba druhým bajtom).
Čo som dostal
Tvárou v tvár tomuto problému som sa rozhodol precvičiť si hranie hier s bitmi a zároveň sa trochu lepšie zoznámiť so štruktúrou Unicode ako celku. Výsledkom bol formát kódovania UTF-C ("C" pre kompaktné), ktorý neminie viac ako 3 bajty na bod kódu a veľmi často vám umožňuje minúť iba jeden bajt navyše pre celý kódovaný riadok. To vedie k tomu, že na mnohých abecedách bez ASCII sa takéto kódovanie ukazuje ako také O 30-60% kompaktnejšie ako UTF-8.
Uviedol som príklady implementácie kódovacích a dekódovacích algoritmov vo forme
Výsledky testov a porovnanie s UTF-8
Tiež som to urobil
Odstránenie nadbytočných bitov
Ako základ som samozrejme bral UTF-8. Prvá a najzreteľnejšia vec, ktorú je možné v ňom zmeniť, je zníženie počtu obslužných bitov v každom byte. Napríklad prvý bajt v UTF-8 vždy začína buď 0
, alebo s 11
- predpona 10
Majú ho iba nasledujúce bajty. Nahradíme predponu 11
na 1
a pre ďalšie bajty predpony úplne odstránime. Čo sa bude diať?
0xxxxxxx
— 1 bajt
10xxxxxx xxxxxxxx
- 2 bajty
110xxxxx xxxxxxxx xxxxxxxx
- 3 bajty
Počkaj, kde je ten štvorbajtový záznam? Ale už to nie je potrebné - pri zápise v troch bajtoch máme k dispozícii 21 bitov a to stačí pre všetky čísla do 0x10FFFF
.
Čo sme tu obetovali? Najdôležitejšia je detekcia hraníc znakov z ľubovoľného miesta vo vyrovnávacej pamäti. Nemôžeme ukázať na ľubovoľný bajt a nájsť z neho začiatok ďalšieho znaku. Toto je obmedzenie nášho formátu, ale v praxi je to zriedka potrebné. Zvyčajne sme schopní prebehnúť buffer od úplného začiatku (najmä ak ide o krátke linky).
Situácia s pokrytím jazykov s 2 bajtmi sa tiež zlepšila: teraz dvojbajtový formát poskytuje rozsah 14 bitov, a to sú kódy až do 0x3FFF
. Číňania majú smolu (ich znaky sa väčšinou pohybujú od 0x4E00
na 0x9FFF
), ale Gruzínci a mnoho ďalších národov sa baví viac - ich jazyky sa tiež zmestia do 2 bajtov na znak.
Zadajte stav kódovača
Zamyslime sa teraz nad vlastnosťami samotných čiar. Slovník najčastejšie obsahuje slová písané znakmi rovnakej abecedy a platí to aj pre mnohé iné texty. Bolo by dobré označiť túto abecedu raz a potom uviesť iba číslo písmena v nej. Uvidíme, či nám pomôže usporiadanie znakov v tabuľke Unicode.
Ako je uvedené vyššie, Unicode sa delí na lietadlo 65536 kódov každý. Ale to nie je veľmi užitočné delenie (ako už bolo povedané, najčastejšie sme v nulovej rovine). Zaujímavejšie je delenie podľa bloky. Tieto rozsahy už nemajú pevnú dĺžku a sú zmysluplnejšie – spravidla každý kombinuje znaky z rovnakej abecedy.
Blok obsahujúci znaky bengálskej abecedy. Bohužiaľ, z historických dôvodov ide o príklad nie príliš hustého balenia – 96 znakov je chaoticky roztrúsených po 128 bodoch blokového kódu.
Začiatky blokov a ich veľkosti sú vždy násobky 16 - to sa robí jednoducho pre pohodlie. Okrem toho veľa blokov začína a končí na hodnotách, ktoré sú násobkami 128 alebo dokonca 256 - napríklad základná azbuka zaberá 256 bajtov od 0x0400
na 0x04FF
. To je celkom výhodné: ak predponu raz uložíme 0x04
, potom je možné do jedného bajtu zapísať ľubovoľný znak cyriliky. Pravdaže, takto prídeme o možnosť vrátiť sa k ASCII (a k akýmkoľvek iným znakom všeobecne). Preto robíme toto:
- Dva bajty
10yyyyyy yxxxxxxx
nielen označovať symbol číslomyyyyyy yxxxxxxx
, ale aj zmeniť aktuálna abeceda nayyyyyy y0000000
(t. j. pamätáme si všetky bity okrem tých najmenej významných Bit 7); - Jeden bajt
0xxxxxxx
toto je znak súčasnej abecedy. Stačí ho pridať k posunu, ktorý sme si zapamätali v kroku 1. Aj keď sme nezmenili abecedu, posun je nulový, takže sme zachovali kompatibilitu s ASCII.
Podobne pre kódy vyžadujúce 3 bajty:
- Tri bajty
110yyyyy yxxxxxxx xxxxxxxx
označte symbol číslomyyyyyy yxxxxxxx xxxxxxxx
, zmeniť aktuálna abeceda nayyyyyy y0000000 00000000
(pamätal si všetko okrem mladších Bit 15) a začiarknite políčko, v ktorom sa práve nachádzame dlhý režim (pri zmene abecedy späť na dvojbajtovú tento príznak vynulujeme); - Dva bajty
0xxxxxxx xxxxxxxx
v dlhom režime je to znak aktuálnej abecedy. Podobne to pridáme s posunom z kroku 1. Rozdiel je len v tom, že teraz čítame dva bajty (pretože sme prešli do tohto režimu).
Znie to dobre: zatiaľ čo teraz potrebujeme zakódovať znaky z rovnakého 7-bitového rozsahu Unicode, minieme na začiatku 1 bajt navyše a celkovo jeden bajt na znak.
Pracuje z jednej zo starších verzií. Už teraz často prekonáva UTF-8, ale stále je čo zlepšovať.
čo je horšie? Po prvé, máme podmienku, a to aktuálny posun abecedy a začiarkavacie políčko dlhý režim. To nás ešte viac obmedzuje: teraz môžu byť rovnaké znaky kódované odlišne v rôznych kontextoch. Napríklad vyhľadávanie podreťazcov bude musieť byť vykonané s ohľadom na túto skutočnosť a nielen porovnávaním bajtov. Po druhé, akonáhle sme zmenili abecedu, pokazilo sa to s kódovaním ASCII znakov (a to nie je len latinka, ale aj základná interpunkcia vrátane medzier) - vyžadujú zmenu abecedy znova na 0, tj. opäť jeden bajt navyše (a potom ďalší, aby sme sa vrátili k nášmu hlavnému bodu).
Jedna abeceda je dobrá, dve sú lepšie
Pokúsme sa trochu zmeniť naše bitové predpony a vtlačiť ešte jednu k trom opísaným vyššie:
0xxxxxxx
— 1 bajt v normálnom režime, 2 v dlhom režime
11xxxxxx
— 1 bajt
100xxxxx xxxxxxxx
- 2 bajty
101xxxxx xxxxxxxx xxxxxxxx
- 3 bajty
Teraz je v dvojbajtovom zázname o jeden dostupný bit menej - kód ukazuje až 0x1FFF
A nie 0x3FFF
. Stále je však výrazne väčší ako v dvojbajtových kódoch UTF-8, väčšina bežných jazykov sa stále zmestí, najvýraznejšia strata vypadla
Aký je tento nový kód? 11xxxxxx
? Toto je malá „skrýša“ s veľkosťou 64 znakov, ktorá dopĺňa našu hlavnú abecedu, preto som ju nazval pomocnou (pomocný) abeceda. Keď prepneme aktuálnu abecedu, kúsok starej abecedy sa stane pomocnou. Napríklad sme prešli z ASCII na azbuku – schránka teraz obsahuje 64 znakov Latinská abeceda, čísla, medzera a čiarka (najčastejšie vkladanie do neASCII textov). Prešli sme späť na ASCII - a hlavná časť azbuky sa stane pomocnou abecedou.
Vďaka prístupu k dvom abecedám dokážeme spracovať veľké množstvo textov s minimálnymi nákladmi na prepínanie abecied (interpunkcia najčastejšie povedie k návratu k ASCII, ale potom získame veľa neASCII znakov z doplnkovej abecedy, bez opätovné prepnutie).
Bonus: predpona podabecedy 11xxxxxx
a výber jeho počiatočného posunu byť 0xC0
, získame čiastočnú kompatibilitu s CP1252. Inými slovami, mnohé (ale nie všetky) západoeurópske texty zakódované v CP1252 budú vyzerať rovnako v UTF-C.
Tu však nastáva problém: ako získať pomocnú z hlavnej abecedy? Môžete ponechať rovnaký offset, ale - bohužiaľ - tu už hrá štruktúra Unicode proti nám. Hlavná časť abecedy sa veľmi často nenachádza na začiatku bloku (napríklad ruské veľké „A“ má kód 0x0410
, hoci cyrilský blok začína s 0x0400
). Keď teda vezmeme prvých 64 znakov do skrýše, môžeme stratiť prístup k zadnej časti abecedy.
Aby som tento problém vyriešil, manuálne som prešiel cez niektoré bloky zodpovedajúce rôznym jazykom a určil som pre ne posun pomocnej abecedy v rámci hlavnej. Latinská abeceda bola ako výnimka vo všeobecnosti preusporiadaná ako základ64.
Záverečné úpravy
Poďme sa už konečne zamyslieť, kde ešte môžeme niečo zlepšiť.
Všimnite si, že formát 101xxxxx xxxxxxxx xxxxxxxx
umožňuje kódovať čísla až do 0x1FFFFF
a Unicode končí skôr, o 0x10FFFF
. Inými slovami, posledný bod kódu bude reprezentovaný ako 10110000 11111111 11111111
. Preto môžeme povedať, že ak je prvý bajt vo forme 1011xxxx
(kde xxxx
väčšie ako 0), potom to znamená niečo iné. Môžete tam napríklad pridať ďalších 15 znakov, ktoré sú neustále k dispozícii na kódovanie do jedného bajtu, ale rozhodol som sa to urobiť inak.
Pozrime sa teraz na tie bloky Unicode, ktoré vyžadujú tri bajty. V podstate, ako už bolo spomenuté, ide o čínske znaky - ale je ťažké s nimi niečo urobiť, je ich 21 tisíc. Ale letela tam aj hiragana a katakana – a už ich nie je toľko, menej ako dvesto. A keďže sme si spomenuli na Japoncov, existujú aj emotikony (v skutočnosti sú v Unicode roztrúsené na mnohých miestach, ale hlavné bloky sú v dosahu 0x1F300
- 0x1FBFF
). Ak sa zamyslíte nad tým, že teraz existujú emotikony, ktoré sú zostavené z niekoľkých kódových bodov naraz (napríklad emodži
Preto vyberieme niekoľko vybraných rozsahov zodpovedajúcich emoji, hiragane a katakane, prečíslujeme ich do jedného súvislého zoznamu a zakódujeme ako dva bajty namiesto troch:
1011xxxx xxxxxxxx
Skvelé: vyššie uvedené emotikony
Skúsme vyriešiť ešte jeden problém. Ako si pamätáme, základná abeceda je v podstate vysoký 6 bitov, ktorý máme na pamäti a lepíme na kód každého ďalšieho dekódovaného symbolu. V prípade čínskych znakov, ktoré sú v bloku 0x4E00
- 0x9FFF
, je to buď bit 0 alebo 1. To nie je príliš pohodlné: budeme musieť neustále prepínať abecedu medzi týmito dvoma hodnotami (t. j. minúť tri bajty). Všimnite si však, že v dlhom režime môžeme od samotného kódu odpočítať počet znakov, ktoré zakódujeme pomocou krátkeho režimu (po všetkých vyššie opísaných trikoch je to 10240 XNUMX) - potom sa rozsah hieroglyfov posunie na 0x2600
- 0x77FF
a v tomto prípade v celom tomto rozsahu bude najvýznamnejších 6 bitov (z 21) rovných 0. Sekvencie hieroglyfov teda budú používať dva bajty na hieroglyf (čo je optimálne pre taký veľký rozsah), bez spôsobuje prepínanie abecedy.
Alternatívne riešenia: SCSU, BOCU-1
Odborníci na Unicode, ktorí si práve prečítali názov článku, vám s najväčšou pravdepodobnosťou pripomenie, že priamo medzi štandardmi Unicode existuje
Úprimne priznávam: o jej existencii som sa dozvedel až potom, čo som bol hlboko ponorený do písania svojho rozhodnutia. Keby som o tom vedel od začiatku, pravdepodobne by som sa pokúsil napísať implementáciu namiesto toho, aby som prišiel s vlastným prístupom.
Zaujímavé je, že SCSU používa nápady veľmi podobné tým, na ktoré som prišiel sám (namiesto konceptu „abecedy“ používajú „okná“ a je ich k dispozícii viac, ako mám ja). Zároveň má tento formát aj nevýhody: je o niečo bližšie k kompresným algoritmom ako kódovacím. Predovšetkým štandard poskytuje veľa reprezentačných metód, ale nehovorí, ako zvoliť optimálnu - na to musí kodér použiť nejaký druh heuristiky. Takže kódovač SCSU, ktorý vytvára dobré balenie, bude zložitejší a ťažkopádnejší ako môj algoritmus.
Pre porovnanie som do JavaScriptu preniesol pomerne jednoduchú implementáciu SCSU - z hľadiska objemu kódu mi to vyšlo porovnateľne s mojím UTF-C, no v niektorých prípadoch bol výsledok o desiatky percent horší (niekedy ho možno prekročí, ale nie príliš). Napríklad texty v hebrejčine a gréčtine boli kódované pomocou UTF-C O 60 % lepšie ako SCSU (pravdepodobne kvôli ich kompaktným abecedám).
Samostatne dodám, že okrem SCSU existuje aj iný spôsob, ako kompaktne reprezentovať Unicode -
Možné vylepšenia
Algoritmus, ktorý som predstavil, nie je dizajnovo univerzálny (pravdepodobne sa moje ciele najviac rozchádzajú s cieľmi Unicode Consortium). Už som spomenul, že bol vyvinutý primárne pre jednu úlohu (ukladanie viacjazyčného slovníka do stromu prefixov) a niektoré jeho funkcie nemusia byť vhodné pre iné úlohy. Ale skutočnosť, že to nie je štandard, môže byť plus - môžete ho ľahko upraviť podľa svojich potrieb.
Napríklad, zrejmým spôsobom sa môžete zbaviť prítomnosti stavu, vytvoriť bezstavové kódovanie – jednoducho neaktualizujte premenné offs
, auxOffs
и is21Bit
v kódovači a dekodéri. V tomto prípade nebude možné efektívne zbaliť sekvencie znakov rovnakej abecedy, ale bude zaručené, že rovnaký znak bude vždy zakódovaný rovnakými bajtmi, bez ohľadu na kontext.
Okrem toho si môžete prispôsobiť kódovač konkrétnemu jazyku zmenou predvoleného stavu - napríklad zameraním na ruské texty, nastavte kódovač a dekodér na začiatku offs = 0x0400
и auxOffs = 0
. To dáva zmysel najmä v prípade bezstavového režimu. Vo všeobecnosti to bude podobné ako pri použití starého osembitového kódovania, ale bez odstránenia možnosti vkladať znaky zo všetkých Unicode podľa potreby.
Ďalšou nevýhodou uvedenou vyššie je, že vo veľkom texte kódovanom v UTF-C neexistuje rýchly spôsob, ako nájsť hranicu znakov najbližšie k ľubovoľnému bajtu. Ak odrežete posledných povedzme 100 bajtov z kódovanej vyrovnávacej pamäte, riskujete, že dostanete odpad, s ktorým nemôžete nič robiť. Kódovanie nie je určené na ukladanie multi-gigabajtových protokolov, ale vo všeobecnosti sa to dá opraviť. Byte 0xBF
sa nikdy nesmie objaviť ako prvý bajt (ale môže byť druhý alebo tretí). Preto pri kódovaní môžete vložiť sekvenciu 0xBF 0xBF 0xBF
každých, povedzme, 10 KB - potom, ak potrebujete nájsť hranicu, bude stačiť skenovať vybraný kus, kým sa nenájde podobná značka. Po poslednom 0xBF
je zaručene začiatok postavy. (Pri dekódovaní bude potrebné túto postupnosť troch bajtov samozrejme ignorovať.)
Sčítanie
Ak ste sa dočítali až sem, gratulujeme! Dúfam, že ste sa rovnako ako ja dozvedeli niečo nové (alebo si osviežili pamäť) o štruktúre Unicode.
Demo stránka. Príklad hebrejčiny ukazuje výhody oproti UTF-8 aj SCSU.
Vyššie popísaný výskum by sa nemal považovať za zásah do noriem. S výsledkami svojej práce som však celkovo spokojný, takže mám z nich radosť
Nakoniec ešte raz upozorním na prípady, v ktorých sa používa UTF-C nestojí za to:
- Ak sú vaše riadky dostatočne dlhé (100-200 znakov). V tomto prípade by ste mali premýšľať o použití kompresných algoritmov, ako je deflate.
- Ak potrebuješ ASCII transparentnosť, to znamená, že je pre vás dôležité, aby kódované sekvencie neobsahovali ASCII kódy, ktoré neboli v pôvodnom reťazci. Tejto potrebe sa dá vyhnúť, ak pri interakcii s rozhraniami API tretích strán (napríklad pri práci s databázou) odošlete výsledok kódovania ako abstraktnú množinu bajtov, a nie ako reťazce. V opačnom prípade riskujete neočakávané zraniteľné miesta.
- Ak chcete rýchlo nájsť hranice znakov s ľubovoľným posunom (napríklad, keď je časť riadku poškodená). Dá sa to urobiť, ale iba skenovaním riadku od začiatku (alebo použitím úpravy opísanej v predchádzajúcej časti).
- Ak potrebujete rýchlo vykonávať operácie s obsahom reťazcov (triediť ich, hľadať v nich podreťazce, spájať). Vyžaduje si to najskôr dekódovanie reťazcov, takže UTF-C bude v týchto prípadoch pomalšie ako UTF-8 (ale rýchlejšie ako kompresné algoritmy). Keďže rovnaký reťazec je vždy zakódovaný rovnakým spôsobom, nie je potrebné presné porovnanie dekódovania a možno ho vykonať po byte.
Update: užívateľ
Zdroj: hab.com