
Kui olete arendaja ja silmitsi valikuga kodeeringu osas, siis peaaegu alati on Ă”ige lahendus Unicode. Konkreetne esitamise meetod sĂ”ltub kontekstist, kuid enamasti on siin samuti universaalne vastus â UTF-8. See on hea seetĂ”ttu, et vĂ”imaldab kasutada kĂ”iki Unicode'i mĂ€rke, kulutamata liialt palju baite enamikul juhtudel. TĂ”si, keelte puhul, mis ei kasuta ainult ladina tĂ€hestikku, tĂ€hendab âliialt vĂ€heâ vĂ€hemalt kaht byte'i sĂŒmboli kohta. Kas on vĂ”imalik paremini, naasmata eelajaloolistesse kodeeringutesse, mis piiravad meid vaid 256 saadaval sĂŒmboliga?
Allpool pakun vĂ€lja oma katse anda sellele kĂŒsimusele vastus ja teostada suhteliselt lihtne algoritm, mis vĂ”imaldab salvestada ridu enamikus maailma keeltes, lisamata neid ĂŒleliigseid koormusi, mis on omased UTF-8-le.
Ăratus. TĂ”in esile mitmeid olulisi juhtnööre: kirjeldatud lahendust ei pakuta universaalse asendusena UTF-8-le, see sobib vaid kitsas nimekirjas juhtudest (millest kirjutan allpool), ja seda ei tohi mingil juhul kasutada kolmandate osapoolte API-dega suhtlemiseks (kes sellest isegi ei tea). Enamasti sobivad suure hulga tekstiliste andmete kompaktseks hoidmiseks ĂŒldotstarbelised tihendamisalgoritmid (nĂ€iteks deflate). Lisaks leidsin juba oma lahenduse loomise kĂ€igus olemasoleva standardi ise Unicode'is, mis lahendab sama ĂŒlesande â see on veidi keerulisem (ja tihti kehvem), kuid siiski vastuvĂ”etud standard, mitte jalgaastatud lahendus. Kannan ka sellest teada.
Unicode ja UTF-8
Alustuseks â paar sĂ”na sellest, mis see ĂŒldiselt on Unicode ja UTF-8.
Kuidas on teada, olid varem populaarsed 8-bitised kodeeringud. Nendega oli kĂ”ik lihtne: 256 sĂŒmbolit saab nummerdada numbritega vahemikus 0 kuni 255, ning numbrid vahemikus 0 kuni 255 on selgelt esitatavad ĂŒhe baidina. Kui minna tagasi algusesse, siis ASCII kodeering piirneb isegi 7 bitiga, seega on selle baidiesituses kĂ”ige vanem bit null, ja enamik 8-bitisi kodeeringuid on sellega ĂŒhilduvad (need erinevad ainult âĂŒlemisesâ osas, kus vanem bit on ĂŒks).
Kuidas eristub Unicode neist kodeeringutest ja miks on sellega seotud korraga mitmed konkreetsed esitused â UTF-8, UTF-16 (BE ja LE), UTF-32? Selgitame jĂ€rjest.
Peamine Unicode'i standard kirjeldab ainult vastavust tĂ€hemĂ€rkide (ja mĂ”nel juhul - tĂ€hemĂ€rkide eraldi komponentide) ja nende numbrite vahel. Ja vĂ”imalikke numbreid selles standardis on vĂ€ga palju - alates 0x00 kuni 0x10FFFF (1 114 112 tĂŒkki). Kui me tahaksime sellises vahemikus arvu muutuja sisse panna, ei piisaks meile ei 1 ega 2 baitist. Ja kuna meie protsessorid ei ole kolmbaitsete arvudega vĂ€ga harjutanud, oleksime sunnitud kasutama terveid 4 baiti ĂŒhe tĂ€hemĂ€rgi jaoks! Just see ongi UTF-32, kuid just selle "rahakulu" tĂ”ttu ei ole see formaat populaarne.
Ănneks ei ole Unicode'i tĂ€hemĂ€rgid korduvad juhuslikult. Nende suur hulk on jaotatud 17 «tasandisse», millest igaĂŒks sisaldab 65536 (0x10000) «koodipunkti». MĂ”isted âkoodipunktâ tĂ€histab siin lihtsalt tĂ€hemĂ€rgi numbrit, mille Unicode on talle mÀÀranud. Kuid nagu eespool mainitud, on Unicode'is nummerdatud mitte ainult eraldi tĂ€hemĂ€rgid, vaid ka nende komponendid ja teenindusmĂ€rkmed (ja mĂ”nikord ei vasta number ĂŒldse millelegi - vĂ”ib-olla ajutiselt, kuid meile pole see niivĂ”rd oluline), seega on korrektsem alati rÀÀkida just numbrite koguarvust, mitte tĂ€hemĂ€rkidest. Siiski, edaspidigi kasutan sageli sĂ”na âtĂ€hemĂ€rkâ, silmas pidades mĂ”istet âkoodipunktâ.

Unicode'i tasandid. Nagu nÀha, on suur osa (tasandid 4 kuni 13) endiselt kasutamata.
Ja kĂ”ige tĂ€helepanuvÀÀrsem on see, et kogu peamine âsisuâ asub nulltasandil, mida nimetatakse "Basic Multilingual Plane". Kui rida sisaldab teksti mĂ”nes kaasaegses keeles (sealhulgas hiina keeles), ei pÀÀse te sellele tasandilt vĂ€lja. Kuid ka teisi Unicode'i osasid ei saa vĂ€lja jĂ€tta - nĂ€iteks emoji asuvad peamiselt jĂ€rgmise tasandi lĂ”pus, "Supplementary Multilingual Plane" (see ulatub alates 0x10000 kuni 0x1FFFF). SeetĂ”ttu kĂ€itub UTF-16 nii: kĂ”ik tĂ€hemĂ€rgid, mis jÀÀvad Basic Multilingual Plane, kodeeritakse ânagu onâ, nende vastav kahekohaline number. Kuid osa numbritest selles vahemikus ei tĂ€hista ĂŒldse konkreetseid tĂ€hemĂ€rke, vaid osutavad sellele, et pĂ€rast seda kaht baidi paari tuleb vaadata veel ĂŒhte - kombineerides nende nelja baidi vÀÀrtused, saame numbri, mis katab kogu lubatud Unicode'i vahemiku. Seda esitusviisi nimetatakse âsuurte paarideksâ - vĂ”ib-olla olete neist kuulnud.
Seega, UTF-16 nĂ”uab ĂŒhe "koodipunkti" jaoks kahte vĂ”i (vĂ€ga harvadel juhtudel) neli baiti. See on parem kui pidev nelja baitide kasutamine, kuid ladina tĂ€hestiku (ja teiste ASCII-mĂ€rkide) puhul kulutab see sellise kodeerimisega pool salvestatud ruumist nullidele. UTF-8 on loodud seda parandama: ASCII vĂ”tab seal nagu varem ainult ĂŒhe bait; koodid alates 0x80 kuni 0x7FF â kahest baitist; alates 0x800 kuni 0xFFFF â kolmest ja alates 0x10000 kuni 0x10FFFF â neljast. Ăhelt poolt on ladina tĂ€hestikul nĂŒĂŒd hĂ€sti: tagasiviidatud ĂŒhildus ASCII-ga, ning jaotus on ĂŒhtlasemalt "jaotatud" 1 kuni 4 baidi vahel. Kuid ladina tĂ€hestikust erinevad tĂ€hestikud, kahjuks, ei saa UTF-16-ga vĂ”rreldes midagi vĂ”ita, ja paljud nĂ”uavad nĂŒĂŒd kolmandat bait; kahe baitiga kirje katab vahemiku 32 korda vĂ€henenud, ulatudes 0xFFFF kuni 0x7FF, ja see ei hĂ”lma enam ei hiina ega nĂ€iteks gruusia keelt. Kirillitsale ja veel viiele tĂ€hestikule â rÔÔm â on vedanud, 2 baiti sĂŒmboli peale.
Kuidas see siis vĂ€lja tuleb? Vaatame, kuidas UTF-8 esindab sĂŒmbolite koode:

Numbrite otseseks esitamiseks on siin kasutatud bitte, tĂ€histatud sĂŒmboliga x. On nĂ€ha, et kahe baitiga kirjes on selliseid bitte vaid 11 (16-st). Esialgsed bitid kannavad siin ainult teeninduslikku funktsiooni. Nelja baitiga kirje puhul on koodipunkti number mÀÀratud tĂ€ielikult 21 bitile 32-st â nĂ€iliselt peaks siin piisama kolmest baitist (mis annab kokku 24 bitti), kuid teenindusmĂ€rgid vĂ”tavad liiga palju ruumi.
Kas see on halb? Tegelikult mitte. Ăhelt poolt â kui me hoolime vĂ€ga salvestusruumist, on meil olemas tihendamisalgoritmid, mis kĂ”rvaldavad kergesti kogu liigse entropia ja ĂŒleliigsuse. Teiselt poolt â Unicode eesmĂ€rk oli anda vĂ”imalikult universaalne kodeerimine. NĂ€iteks vĂ”ime UTF-8-s kodeeritud stringi usaldada koodile, mis töötas varem ainult ASCII-ga, ja ei muretse, et seal nĂ€eb sĂŒmbolit ASCII vahemikust, mida seal tegelikult ei ole (sest UTF-8-s on kĂ”ik baitid, mis algavad nullbitiga â need ongi ASCII). Ja kui me tahame Ă€kki suurt stringist mĂ”ne vĂ€ikese saba Ă€ra lĂ”igata, ilma et me seda algusest peale dekodeeriksime (vĂ”i taastaksime teavet pĂ€rast kahjustatud osa) â ei ole meil keeruline leida seda nihke kohta, kust mĂ”ni sĂŒmbol algab (piisab, kui mööda minna baitsidest, millel on bittide eellane 10).
Miks siis midagi uut vÀlja mÔelda?
Samas ajal esineb harva olukordi, kus kokkusurumisalgoritmid nagu deflate ei ole hĂ€sti rakendatavad, kuid soov on saavutada kompaktsed stringide salvestamise vormid. Isiklikult olen sellise ĂŒlesande ees seisnud, kui olen mĂ”elnud suure sĂ”navara jaoks, mis sisaldab sĂ”nu erinevates keeltes. Ăhelt poolt on iga sĂ”na vĂ€ga lĂŒhike, seega on selle kokkusurumine ebaefektiivne. Teiselt poolt - puu realiseerimine, millele ma mĂ”tlesin, eeldas, et iga salvestatud stringi bait genereeris eraldi puu tipu, mistĂ”ttu oli nende arvu vĂ€hendamine vĂ€ga kasulik. Minu teegis (nagu ka , millele see pĂ”hineb) selline probleem lahendatakse lihtsalt - stringid, mis on pakitud -sĂ”naraam, sĂ€ilitatakse seal . Kuid nagu on lihtne mĂ”ista, töötab see hĂ€sti ainult piiratud tĂ€hestikuga - hiina stringi sellesse sĂ”naraamatusse ei saa juba paigutada.
Eraldi mĂ€rgin veel ĂŒhe ebameeldiva nĂŒansi, mis tekib UTF-8 kasutamisel sellises andmestruktuuris. Ălaltoodud pildilt on nĂ€ha, et kui sĂŒmbolit salvestatakse kahe baitina, siis selle numbri bitid ei paikne jĂ€rjestikku, vaid on katkenud paari bitti 10 vahepeal: 110xxxxx 10xxxxxx. Selle tĂ”ttu, kui sĂŒmboli koodis ĂŒletatakse teise baidi madalamad 6 bitti (st toimub ĂŒleminek 10111111 â 10000000), muutub ka esimene bait. Tulemuseks on, et tĂ€ht âĐżâ tĂ€histatakse baitidega 0xD0 0xBF, ja jĂ€rgmine âŃâ juba 0xD1 0x80. Prefikspuus pĂ”hjustab see vanema tipu jagunemise kaheks - ĂŒheks prefiksi 0xD0, ja teiseks 0xD1 (kuigi kogu kirillitsa vĂ”iks kodeerida ainult teise baidi kaudu).
Mida ma sain
Selle ĂŒlesande ees seistes otsustasin natuke mĂ€ngida bittidega ja samal ajal tutvuda paremini Unicode'i struktuuriga. Tulemuseks sai koodimisformaat UTF-C (âCâ tĂ€histab compact), mis kulutab ĂŒhe koodipunkti jaoks mitte ĂŒle 3 baidi ning sageli vĂ”imaldab kulutada vaid ĂŒhe lisabait terve kodeeritud stringi peale.See toob kaasa selle, et paljude mitte-ASCII tĂ€hestike korral osutub selline kodeering 30-60% kompaktselt kui UTF-8..
Olen vormistatud koodimise ja dekodeerimise algoritmide nĂ€idised , vĂ”ite neid oma koodis vabalt kasutada. Kuid tahaksin siiski rĂ”hutada, et teataval mÀÀral jÀÀb see formaat âjalgrattaksâ ja ma ei soovita seda kasutada ilmse arusaamata, miks see teile vajalik on. See on siiski pigem eksperiment kui tĂ”sine âUTF-8 tĂ€iustamineâ. Sellegipoolest on sealne kood kirjutatud korralikult, kompaktsetena, koos rohkete kommentaaride ja katsetega.

Testide tulemused ja vÔrdlus UTF-8-ga
Lisaks tegin ma , kus saab hinnata algoritmi tööd, ja hiljem rÀÀgin selle pĂ”himĂ”tetest ja vĂ€ljatöötamisprotsessist ĂŒksikasjalikumalt.
Eemaldame ĂŒleliigsed bitid
Aluseks vĂ”tsin loomulikult UTF-8. Esimene ja ilmselge asi, mida seal muuta saab â vĂ€hendada teenindusbitide arvu igas baitis. NĂ€iteks hakkab esimene bait UTF-8-s alati kas 0, vĂ”i 11 â ja prefiks 10 on ainult jĂ€rgnevate baitide jaoks. Asendame prefiksi 11 . Tundub, et 1, ja jĂ€rgmiste baitide puhul eemaldame prefiksid tĂ€ielikult. Mis juhtub?
0xxxxxxx â 1 bait
10xxxxxx xxxxxxxx â 2 baiti
110xxxxx xxxxxxxx xxxxxxxx â 3 baiti
Peatus, aga kus on nelja-baitline kirje? See pole enam vajalik â kolme baitiga kirjutamisel on meil nĂŒĂŒd 21 bitti ja seda piisab kuni 0x10FFFF.
Mida me siin ohverdasime? Peamine â sĂŒmbolite piiride avastamine juhuslikust buffri kohast. Me ei saa torkida juhuslikku baidi ja leida sealt jĂ€rgmise sĂŒmboli algust. See on meie formaadi piirang, kuid praktikas ei esine sellist vajadust tihti. Ăldiselt saame pĂŒsida buffri algusest alates (eriti kui on tegemist lĂŒhikeste ridadega).
Kaks baitide katvuse olukord on ka paranenud: nĂŒĂŒd annab kaheettevĂ”tmise formaat 14 bitist vahemiku, mis katab koodid kuni 0x3FFF. Hiinlastel ei vea (nende ideogrammid asuvad peamiselt vahemikus 0x4E00 kuni 0x9FFF), kuid gruusia rahvas ja paljud teised on nĂŒĂŒd Ă”nnelikumad â nende keeled mahuvad samuti 2 baiti sĂŒmboli kohta.
SissevÔttes oleme dekooder
NĂŒĂŒd mĂ”tleme ridade omadustele. SĂ”naraamatus on sageli sĂ”nad, mis on kirjutatud ĂŒhe tĂ€htede alfabeediga, ja paljude teiste tekstide puhul on see samuti tĂ”si. Oleks hea korra nĂ€idata see alfabet, ja seejĂ€rel nĂ€idata ainult tĂ€htede numbrit selle sees. Vaadakem, kas Unicode'i tabelis olevad sĂŒmbolite asukohad aitavad meid.
Nagu ĂŒlalpool mainitud, on Unicode jagatud tasanditeks koodide iga 65536. Kuid see ei ole eriti kasulik jaotus (nagu juba mainitud, oleme enamasti nulltasemel). Huvi pakkub jaotus plokkideks. Need vahemikud ei ole enam fikseeritud pikkusega ja omavad rohkem tĂ€hendust â reeglina koondavad nad sama tĂ€hestiku sĂŒmbolid.

Plokk, mis sisaldab bengali tĂ€hestiku sĂŒmboleid. Kahjuks on ajaloolistel pĂ”hjustel see nĂ€ide ĂŒsna ebaĂŒhtlasest pakkimisest â 96 sĂŒmbolit on juhuslikult hajutatud 128 koodipunkti vahel plokis.
Plokkide algused ja nende suurused on alati 16-kordsed â see on lihtsalt mugavuse tĂ”ttu. Lisaks, paljud plokid algavad ja lĂ”ppevad vÀÀrtustes, mis on 128 vĂ”i isegi 256 kordne â nĂ€iteks, pĂ”hikirillika hĂ”ivab 256 baiti alates 0x0400 kuni 0x04FF. See on ĂŒsna mugav: kui salvestame kordumatu prefiksi 0x04, siis saame igat kirillika sĂŒmbolit kirjutada ĂŒhe baitiga. TĂ”si, me kaotame vĂ”imaluse tagasi pöörduda ASCII (ja ĂŒkskĂ”ik milliste teiste sĂŒmbolite) juurde. Seega teeme jĂ€rgmist:
- Kaks baiti
10yyyyyy yxxxxxxxei tĂ€hista mitte ainult sĂŒmbolit numbrigayyyyyy yxxxxxxx, vaid muudavad kehtivat tĂ€hestikku . Tundub, etyyyyyy y0000000(st me salvestame kĂ”ik bitid, vĂ€lja arvatud madalamad 7 bitti); - Ăks bait
0xxxxxxxon kehtiva tĂ€hestiku sĂŒmbol. Selle tuleb lihtsalt liita selle nihkega, mille me salvestasime sammul 1. Seni, kuni me tĂ€hestikku ei muuda, on nihke vÀÀrtus null, seega oleme ASCII-ga ĂŒhilduvad.
Sarnaselt koodide puhul, mis nÔuavad 3 bait:
- Kolm baiti
110yyyyy yxxxxxxx xxxxxxxxtĂ€histavad sĂŒmbolit numbrigayyyyyy yxxxxxxx xxxxxxxx, muudab kehtivat tĂ€hestikku . Tundub, etyyyyyy y0000000 00000000(salvestame kĂ”ik, vĂ€lja arvatud madalamad 15 bitti), ja seab mĂ€rkeruumi, et oleme nĂŒĂŒd pikamas reĆŸiimis (tĂ€hestiku tagasi keeramisel kahekaupa, me tĂŒhistame selle mĂ€rkeruumi); - Kaks baiti
0xxxxxxx xxxxxxxxpikamas reĆŸiimis on see kehtiva tĂ€hestiku sĂŒmbol. Samuti liidame selle sammul 1 saadud nihkega. KĂ”ik erinevused on vaid selles, et nĂŒĂŒd me loeme kahte baiti (sest me oleme sellesse reĆŸiimi ĂŒle lĂ€inud).
Tundub hea: nĂŒĂŒd, seni kuni peame kodeerima sĂŒmboleid samas 7-bitises Unicodeâi vahemikus, kulutame ĂŒhe lisabaiti alguses ja vaid ĂŒhe baidi igas sĂŒmbolis.

Ăhe varase versiooni töö. Juba sageli ĂŒletab UTF-8, kuid veel on tĂ€iustamiseks ruumi.
Mis on halvenenud? Esiteks, meil on nĂŒĂŒd olek, nimelt kehtiva tĂ€hestiku nihke ja mĂ€rkeruumi pikamas reĆŸiimis. See, this additionally restricts us: now the same characters can be encoded differently in different contexts. For example, substring searches will have to take this into account, rather than just comparing bytes. Secondly, once we switched the alphabet, we faced issues with the encoding of ASCII characters (which include not only the Latin alphabet but also basic punctuation, including spaces)âthey require a re-switch of the alphabet at 0, meaning an extra byte again (and then another to return to our main one).
One alphabet is good, two is better
Let's try to slightly change our bit prefixes by squeezing in one more alongside the three described above:
0xxxxxxx â 1 byte in normal mode, 2 in long mode
11xxxxxx â 1 bait
100xxxxx xxxxxxxx â 2 baiti
101xxxxx xxxxxxxx xxxxxxxx â 3 baiti

Now with two-byte encoding, there's one less available bitâcode points fit up to 0x1FFF, mitte 0x3FFF. However, it's still significantly more than in two-byte UTF-8 codes; the majority of common languages still fit in, the most noticeable loss is from ja , the Japanese are in sadness.
So what is the new code 11xxxxxx? ĐŃĐŸ ĐœĐ”Đ±ĐŸĐ»ŃŃĐŸĐč «загаŃĐœĐžĐș» ŃĐ°Đ·ĐŒĐ”ŃĐŸĐŒ ĐČ 64 ŃĐžĐŒĐČĐŸĐ»Đ°, ĐŸĐœ ĐŽĐŸĐżĐŸĐ»ĐœŃĐ”Ń ĐœĐ°Ń ĐŸŃĐœĐŸĐČĐœĐŸĐč алŃаĐČĐžŃ, ĐżĐŸŃŃĐŸĐŒŃ Ń ĐœĐ°Đ·ĐČал Đ”ĐłĐŸ ĐČŃĐżĐŸĐŒĐŸĐłĐ°ŃДлŃĐœŃĐŒ (auxiliary) alphabet. When we switch the current alphabet, a piece of the old alphabet becomes auxiliary. For example, switching from ASCII to Cyrillic means we now have 64 characters in the 'warehouse' containing the Latin alphabet, digits, a space, and a comma (the most common insertions in non-ASCII texts). Switching back to ASCII will make the main part of Cyrillic the auxiliary alphabet.
Thanks to access to two alphabets, we can handle a larger amount of text with minimal switching costs (punctuation will often lead to a return to ASCII, but after that, many non-ASCII characters will be retrieved from the additional alphabet, without re-switching).
Bonus: by designating the additional alphabet with a prefix 11xxxxxx and setting its initial offset to 0xC0, we achieve partial compatibility with CP1252. In other words, many (but not all) Western European texts encoded in CP1252 will appear the same in UTF-C.
Here, however, arises the challenge: how to derive an auxiliary alphabet from the main one? You can keep the same offset, butâunfortunatelyâUnicode structure plays against us here. Very often the main part of the alphabet is not at the beginning of the block (for example, the uppercase 'A' in Russian has code 0x0410, kuigi tsĂŒroliitne blokk algab 0x0400). Nii et, vĂ”ttes âvarustusseâ esimesed 64 sĂŒmbolit, vĂ”ime kaotada ligipÀÀsu tĂ€hestiku lĂ”pupoolele.
Selle probleemi lahendamiseks kĂ€isin ma kĂ€sitsi lĂ€bi mĂ”ned plokid, mis vastavad erinevatele keeltele, ja mĂ€rkisin neile pĂ”hiallika sees abialfabeedi nihke. Ladina tĂ€hestikku, erandina, ĂŒmber jĂ€rjestasin hoopis base64 sarnaselt.

Viimased puudutused
MÔelgem lÔpetuseks, kus me saame veel midagi tÀiustada.
Pane tĂ€hele, et formaat 101xxxxx xxxxxxxx xxxxxxxx lubab kodeerida numbreid kuni 0x1FFFFF, kuid Unicode lĂ”peb varem, number 0x10FFFF. TeisisĂ”nu, viimane koodipunkt esindatakse kui 10110000 11111111 11111111. Nii et, me vĂ”ime öelda, et kui esimene bait on kujul 1011xxxx kui xxxx suurem kui 0), siis see tĂ€histab midagi muud. NĂ€iteks vĂ”ib sinna juurde lisada veel 15 sĂŒmbolit, mis on pidevalt koodimiseks ĂŒhe baidi jagu, kuid ma otsustasin kĂ€ituda teisiti.
Vaadakem neid Unicode'i plokke, mis praegu nĂ”uavad kolme bait: enamasti, nagu mainitud, on need hiina ideogrammid â aga nendega on raske midagi teha, neid on 21 tuhat. Kuid sinna on juhtunud ka hiragana ja katakana â neid on juba vĂ€hem, alla kahesaja. Ja kuna me juba jaapanlastest rÀÀkisime â seal on ka emotikonid (tegelikult on neid palju jaotatud Unicode'is, kuid pĂ”hiplokid on vahemikus 0x1F300 â 0x1FBFF). Kui mĂ”elda, et praegu on olemas emotikonid, mis koosnevad mitmest koodipunktist (nĂ€iteks emotikon âââ koosneb lausa 7 koodist!), siis hakkab tĂ”eliselt kurb olema raisata igaĂŒhele kolm bait (7Ă3 = 21 bait ĂŒhe sĂŒmboli eest, see on Ă”udne).
SeetĂ”ttu valime vĂ€lja mĂ”ned valitud vahemikud, mis vastavad emotikonidele, hiraganale ja katakanale, nummerdame need ĂŒhte pidevasse loetellu ja kodeerime kahena baidina kolmest:
1011xxxx xxxxxxxx
SuurepĂ€rane: eelmainitud emotikon âââ, mis koosneb 7 koodipunktist, kasutab UTF-8-s 25 baidi, kuid mahutasime selle 14 kahe baitiga igale koodipunktile). Muide, Habra keeldus seda töötlema (nii vanas kui ka uues toimetajas), nii et tuli see pildina sisestada.
Proovime parandada veel ĂŒhte probleemi. Kuidas me mĂ€letame, et pĂ”hialfabeet on sisuliselt ĂŒlemised 6 bitti, mida me peame meeles ja kinnitame iga dekooditava sĂŒmboli koodi. Hiina hieroglĂŒĂŒfide puhul, mis asuvad plokis 0x4E00 â 0x9FFF, on see kas bit 0 vĂ”i 1. See pole vĂ€ga mugav: meil tuleb pidevalt vahetada tĂ€hestikku nende kahe vÀÀrtuse vahel (st kasutada kolm bitti). Kuid mĂ€rkame, et pikemas reĆŸiimis saame koodist lahutada sĂŒmbolite arvu, mida kodeerime lĂŒhikeses reĆŸiimis (aine eelneva kirjeldatud nipi jĂ€rgi 10240) â siis nihkub hieroglĂŒĂŒfide vahemik 0x2600 â 0x77FF, ja sel juhul on kogu selle vahemiku ĂŒlemised 6 bitti (21-st) vĂ”rdsed 0-ga. Seega kasutavad hieroglĂŒĂŒfide jĂ€rjestused iga hieroglĂŒĂŒfi jaoks kahte bitti (mis on suurte vahemike jaoks optimaalselt), pĂ”hjustamata tĂ€hestiku vahetust.
Alternatiivsed lahendused: SCSU, BOCU-1
Unicode'i asjatundjad, lugedes ainult artikli nime, kiirustavad tÔenÀoliselt meelde tuletama, et Unicode'i standardite seas on (SCSU), mis kirjeldab kodeerimise meetodit, mis on vÀga sarnane artiklis kirjeldatuga.
Tunnistan ausalt: tema olemasolust sain ma teada alles pĂ€rast seda, kui olin sĂŒgavalt oma lahenduse kirjutamisega sisse elanud. Kui oleksin sellest alguses teada, oleksin ilmselt proovinud kirjutada tema rakenduse, mitte vĂ€lja mĂ”elda oma lĂ€henemist.
Huvitav on see, et SCSU kasutab ideid, mis on vĂ€ga sarnased sellele, milleni ma ise jĂ”udsin (seal, kus kasutatakse âaknaidâ tĂ€hestiku asemel, on neid rohkem, kui mul on). Samas on sellel formaadil ka miinuseid: see on veidi lĂ€hemal pakkimisalgoritmidele, mitte kodeerimisele. EelkĂ”ige annab standard palju esitlusviise, kuid ei ĂŒtle, kuidas neist parimat valida â selleks peab kodeerija kasutama mingeid heuristikume. Seega on SCSU kodeerija, mis pakub head pakkimist, keerulisem ja mahukam kui minu algoritm.
VĂ”rdluseks tĂ”in suhteliselt lihtsa SCSU rakenduse JavaScriptis â koodi mahult oli see vĂ”rreldav minu UTF-C-lahendusega, kuid mĂ”nel juhul nĂ€itas see tulemusi, mis olid kĂŒmnete protsentide vĂ”rra halvemad (mĂ”nikord vĂ”ib see ka ĂŒletada, kuid mitte palju). NĂ€iteks heebrea ja kreeka tekstid kodeeris UTF-C lausa 60% paremini kui SCSU (tĂ”enĂ€oliselt tĂ€nu nende kompaktsetele tĂ€hestikele).
Lisaks mainin, et lisaks SCSU-le on olemas ka teine kompaktne Unicode'i esitusviis â , kuid selle eesmĂ€rk on saavutada ĂŒhilduvus MIME-iga (mida mul ei olnud vaja) ning see kasutab veidi teistsugust kodeerimise lĂ€henemist. Ma ei ole selle tĂ”husust hinnanud, kuid mulle tundub, et see ei saa olla efektiivsem kui SCSU.
VÔimalikud tÀiustused
Kasutatud algoritm ei ole disaini poolest universaalne (selle tĂ”ttu lahknevad mu eesmĂ€rgid kĂ”ige rohkem Unicode'i konsortsiumi eesmĂ€rkidest). Olen juba maininud, et see töötati vĂ€lja peamiselt ĂŒhe ĂŒlesande jaoks (mitmekeelse sĂ”naraamatu salvestamine prefiksipuu kujul), ja mĂ”ned selle omadused vĂ”ivad teiste ĂŒlesannete jaoks mitte sobida. Kuid fakt, et see ei ole standard, vĂ”ib olla ka positiivne â saate seda hĂ”lpsasti oma vajadustele kohandada.
NĂ€iteks on ilmne, et vĂ”imalik on eemaldada oleku olemasolu, muuta kodeerimine riistadesse mitteoluliseks â lihtsalt mitte vĂ€rskendada muutujaid kingad, auxOffs ja is21Bit kodeerijas ja dekodeerijas. Sellisel juhul ei saa efektiivselt pakkida sĂŒmboolsete jĂ€rjestusi, kuid on garantii, et sama sĂŒmbol kodeeritakse alati samade baitidega, sĂ”ltumata kontekstist.
Lisaks vĂ”ib kodeerijat kohandada konkreetse keele jĂ€rgi, muutes vaikeseisundi â nĂ€iteks, suunates Venemaa tekstide peale, seadistada alguses kodeerijat ja dekodeerijat offs = 0x0400 ja auxOffs = 0. See on eriti mĂ”istlik just staatiliste reĆŸiimide korral. Ăldiselt sarnaneb see vana 8-bitise kodeerimise kasutamisega, kuid ei vĂ€lista vĂ”imalust vajadusel lisada sĂŒmboleid kogu Unicode'ist.
Veel ĂŒks varem mainitud puudus â mahukas tekstis, mis on kodeeritud UTF-C-s, ei ole kiiret viisi, kuidas leida sĂŒmboli piir, mis asub juhusliku baidi lĂ€hedal. Kui lĂ”ikate kodeeritud puhverdamist viimased, ĂŒtleme, 100 baidi, riskite saada prĂŒgi, millega ei saa midagi teha. Suurte logide hoidmiseks ei ole kodeerimine mĂ”eldud, kuid seda on vĂ”imalik parandada. Bait 0xBF ei tohi kunagi esimesena esineda (aga see vĂ”ib olla teine vĂ”i kolmas). SeetĂ”ttu saab kodeerimise kĂ€igus lisada jĂ€rjestuse 0xBF 0xBF 0xBF iga, ĂŒtleme, 10 Kb â siis piisab valitud osa skaneerimisest, et leida piir, kuni sarnane marker on leitud. Viimane 0xBF garantereeritult on sĂŒmboli algus. (Dekodeerimisel tuleb see kolmest baitist koosnev jĂ€rjestus muidugi ignoreerida.)
KokkuvÔtteks
Kui olete siiani lugenud â Ă”nnitleme! Loodan, et olete nagu mina midagi uut Ă”ppinud (vĂ”i vĂ€rskendanud mĂ€lu) Unicode'i seadistusest.

Demonstratsioonileht. Heebrea nÀitel on nÀha eelistusi nii UTF-8 kui ka SCSU ees.
Ărge kĂ€sitlege eespool toodud uurimusi standardite rikkumisena. Siiski olen ĂŒldiselt tulemuste ĂŒle rahul ja seetĂ”ttu olen rÔÔmuga : nĂ€iteks JS-raamatukogu minimeeritud kujul kaalub vaid 1710 baiti (ja loomulikult ei ole tal sĂ”ltuvusi). Nagu ma enne mainisin, on selle tööga vĂ”imalik tutvuda (seal on ka tekstide komplekt, millega seda saab vĂ”rrelda UTF-8 ja SCSU-ga).
LÔpetuseks tuletan veel kord meelde olukordi, kus UTF-C-d ei tohiks kasutada:
- Kui teie stringid on piisavalt pikad (100â200 sĂŒmbolit). Sel juhul tasub mĂ”elda, kas rakendada kompressioonialgoritme nagu deflate.
- Kui teil on vajalik ASCII lÀbitavus, see tÀhendab, et teie jaoks on oluline, et kodeeritud jÀrjestustes ei oleks ASCII-koode, mis ei olnud algses stringis. Selle vajaduse saab vÀltida, kui edastate kodeerimise tulemuse kui abstraktset baitide kogumit, mitte stringidena, kui suhtlete vÀlistest API-dest (nt töötades andmebaasidega). Vastasel juhul riskite ettenÀgematute haavatavuste ilmnemisega.
- Kui soovite olla suuteline kiiresti leidma sĂŒmbolite piire suvalise nihkega (nĂ€iteks kui osa stringist on kahjustatud). Seda saab teha, kuid ainult skaneerides stringi algusest (vĂ”i rakendades eelmises jaotises kirjeldatud tĂ€iustust).
- Kui teil on vaja kiiresti teha toiminguid stringide sisus (sorteerida neid, otsida alamstringe, konkateerida). Sel juhul tuleb stringid esmalt dekodeerida, seega on UTF-C nendes olukordades aeglasem kui UTF-8 (aga kiirem kompressioonialgoritmidest). Kuna sama string kodeeritakse alati ĂŒhtemoodi, ei nĂ”ua tĂ€pne dekodeerimise vĂ”rdlemine selle tegemist baitide kaupa.
Uuendus: kasutaja Jagasin graafiku, mis rĂ”hutab UTF-C rakenduse piire. NĂ€ha on, et UTF-C on efektiivsem kui ĂŒldotstarbelise tihendamise algoritm (LZW variatsioonid), seni kuni tihendatav tekst on lĂŒhem ~140 sĂŒmbolit (kuigi pean mĂ€rkima, et vĂ”rdlus viidi lĂ€bi ĂŒhe teksti pĂ”hjal; teiste keelte puhul vĂ”ib tulemus erineda).

Allikas: habr.com
