{"id":95911,"date":"2020-10-05T01:42:09","date_gmt":"2020-10-04T23:42:09","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/eshhyo-odin-velosiped-hranim-yunikodnye-stroki-na-30-60-kompaktnee-chem-utf-8"},"modified":"2020-10-05T01:42:09","modified_gmt":"2020-10-04T23:42:09","slug":"eshhyo-odin-velosiped-hranim-yunikodnye-stroki-na-30-60-kompaktnee-chem-utf-8","status":"publish","type":"post","link":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/eshhyo-odin-velosiped-hranim-yunikodnye-stroki-na-30-60-kompaktnee-chem-utf-8","title":{"rendered":"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/56c10bad377127b711bdb204ee300772.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPokud jste v\u00fdvoj\u00e1\u0159 a m\u00e1te za \u00fakol vybrat k\u00f3dov\u00e1n\u00ed, t\u00e9m\u011b\u0159 v\u017edy bude spr\u00e1vn\u00fdm \u0159e\u0161en\u00edm Unicode. Konkr\u00e9tn\u00ed zp\u016fsob reprezentace z\u00e1vis\u00ed na kontextu, ale \u010dasto je zde tak\u00e9 univerz\u00e1ln\u00ed odpov\u011b\u010f \u2014 UTF-8. Je dobr\u00e9 t\u00edm, \u017ee umo\u017e\u0148uje pou\u017e\u00edvat v\u0161echny znaky Unicode, ani\u017e by to st\u00e1lo <em>p\u0159\u00edli\u0161<\/em> mnoho bajt\u016f ve v\u011bt\u0161in\u011b p\u0159\u00edpad\u016f. Pravda, pro jazyky pou\u017e\u00edvaj\u00edc\u00ed nejen latinku, \"ne p\u0159\u00edli\u0161 mnoho\" znamen\u00e1 minim\u00e1ln\u011b <strong>dva bajty na znak<\/strong>. Lze to ud\u011blat l\u00e9pe, ani\u017e bychom se vraceli k prehistorick\u00fdm k\u00f3dov\u00e1n\u00edm, kter\u00e1 n\u00e1s omezuj\u00ed na pouh\u00fdch 256 dostupn\u00fdch znak\u016f?<\/p>\n<p>N\u00ed\u017ee nab\u00edz\u00edm, abyste se sezn\u00e1mili s m\u00fdm pokusem odpov\u011bd\u011bt na tuto ot\u00e1zku a realizovat pom\u011brn\u011b jednoduch\u00fd algoritmus, kter\u00fd umo\u017e\u0148uje ukl\u00e1dat \u0159\u00e1dky ve v\u011bt\u0161in\u011b sv\u011btov\u00fdch jazyk\u016f, ani\u017e bychom p\u0159id\u00e1vali tu nadbyte\u010dnost, kter\u00e1 je v UTF-8.<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p><i>Disklaimer.<\/i> Hned ud\u011bl\u00e1m n\u011bkolik d\u016fle\u017eit\u00fdch pozn\u00e1mek: <strong>popisovan\u00e9 \u0159e\u0161en\u00ed nen\u00ed navrhov\u00e1no jako univerz\u00e1ln\u00ed n\u00e1hrada za UTF-8<\/strong>, hod\u00ed se pouze pro \u00fazk\u00fd seznam p\u0159\u00edpad\u016f (o nich n\u00ed\u017ee) a rozhodn\u011b by se nem\u011blo pou\u017e\u00edvat pro interakci se t\u0159et\u00edmi API (kter\u00e9 o n\u011bm ani nev\u011bd\u00ed). Nej\u010dast\u011bji pro kompaktn\u00ed ukl\u00e1d\u00e1n\u00ed velk\u00fdch objem\u016f textov\u00fdch dat vyhovuj\u00ed algoritmy obecn\u00e9 komprese (nap\u0159\u00edklad deflate). Krom\u011b toho jsem ji\u017e v procesu vytv\u00e1\u0159en\u00ed sv\u00e9ho \u0159e\u0161en\u00ed na\u0161el existuj\u00edc\u00ed standard v samotn\u00e9m Unicode, kter\u00fd \u0159e\u0161\u00ed stejn\u00fd probl\u00e9m \u2014 je o n\u011bco slo\u017eit\u011bj\u0161\u00ed (a \u010dasto hor\u0161\u00ed), ale p\u0159esto je p\u0159ijat\u00fdm standardem, nikoli zkolabovan\u00fdm na kolen\u011b. O n\u011bm tak\u00e9 budu hovo\u0159it.<\/p>\n<h2>O Unicode a UTF-8<\/h2>\n<p>\nNejd\u0159\u00edve \u2014 n\u011bkolik slov o tom, co v\u016fbec je <strong>Unicode<\/strong> a <strong>UTF-8<\/strong>.<\/p>\n<p>Jak v\u00edme, d\u0159\u00edve m\u011bly popularitu 8bitov\u00e9 k\u00f3dov\u00e1n\u00ed. S nimi to bylo jednoduch\u00e9: 256 znak\u016f lze \u010d\u00edslovat \u010d\u00edsly od 0 do 255, a \u010d\u00edsla od 0 do 255 jsou o\u010dividn\u011b reprezentovateln\u00e1 v jednom bajtu. Pokud se vr\u00e1t\u00edme ke sam\u00fdm po\u010d\u00e1tk\u016fm, k\u00f3dov\u00e1n\u00ed ASCII je omezeno na 7 bit\u016f, a proto je nejvy\u0161\u0161\u00ed bit v jeho bajtov\u00e9 reprezentaci roven nule, a v\u011bt\u0161ina 8bitov\u00fdch k\u00f3dov\u00e1n\u00ed je s n\u00edm kompatibiln\u00ed (li\u0161\u00ed se pouze v \"horn\u00ed\" \u010d\u00e1sti, kde je nejvy\u0161\u0161\u00ed bit jedna).<\/p>\n<p>\u010c\u00edm se tedy od t\u011bchto k\u00f3dov\u00e1n\u00ed li\u0161\u00ed Unicode a pro\u010d je s n\u00edm spojeno hned n\u011bkolik konkr\u00e9tn\u00edch reprezentac\u00ed \u2014 UTF-8, UTF-16 (BE a LE), UTF-32? Probereme to postupn\u011b.<\/p>\n<p>Z\u00e1kladn\u00ed standard Unicode popisuje pouze vztah mezi znaky (a v n\u011bkter\u00fdch p\u0159\u00edpadech \u2014 jednotliv\u00fdmi komponenty znak\u016f) a jejich \u010d\u00edsly. A mo\u017en\u00fdch \u010d\u00edsel v tomto standardu je velmi mnoho \u2014 od <code><b>0x00<\/b><\/code> do <code><b>0x10FFFF<\/b><\/code> (1 114 112 kus\u016f). Kdybychom cht\u011bli um\u00edstit \u010d\u00edslo v tomto rozsahu do prom\u011bnn\u00e9, ani 1, ani 2 bajty by n\u00e1m nesta\u010dily. A proto\u017ee na\u0161e procesory nejsou p\u0159\u00edli\u0161 uzp\u016fsobeny pro pr\u00e1ci s t\u0159\u00edbajtov\u00fdmi \u010d\u00edsly, byli bychom nuceni pou\u017e\u00edt cel\u00fdch 4 bajty na jeden znak! T\u00edm je UTF-32, ale pr\u00e1v\u011b kv\u016fli t\u00e9to \"rozma\u0159ilosti\" tento form\u00e1t nen\u00ed popul\u00e1rn\u00ed.<\/p>\n<p>Na\u0161t\u011bst\u00ed jsou znaky v r\u00e1mci Unicode uspo\u0159\u00e1d\u00e1ny ne n\u00e1hodn\u011b. V\u0161echno jejich mno\u017estv\u00ed je rozd\u011bleno do 17 \u201e<em>plo\u0161in<\/em>\u201c, z nich\u017e ka\u017ed\u00e1 obsahuje 65536 (<code><b>0x10000<\/b><\/code>) \u00ab<em>k\u00f3dov\u00fdch bod\u016f<\/em>\u201c. Pojem \u201ek\u00f3dov\u00fd bod\u201c zde \u2014 to je jednodu\u0161e <em>\u010d\u00edslo znaku<\/em>, kter\u00e9 mu bylo p\u0159i\u0159azeno Unicode. Ale, jak bylo \u0159e\u010deno v\u00fd\u0161e, v Unicode jsou \u010d\u00edslov\u00e1ny nejen jednotliv\u00e9 znaky, ale tak\u00e9 jejich komponenty a slu\u017eebn\u00ed ozna\u010den\u00ed (a n\u011bkdy \u010d\u00edslo v\u016fbec nic neodpov\u00edd\u00e1 \u2014 mo\u017en\u00e1 do\u010dasn\u011b, ale pro n\u00e1s to nen\u00ed tak d\u016fle\u017eit\u00e9), proto je spr\u00e1vn\u011bj\u0161\u00ed v\u017edy mluvit pr\u00e1v\u011b o po\u010dtu samotn\u00fdch \u010d\u00edsel, a ne znak\u016f. Nicm\u00e9n\u011b d\u00e1le pro stru\u010dnost \u010dasto budu pou\u017e\u00edvat slovo \u201eznak\u201c, kdy mysl\u00edm term\u00edn \u201ek\u00f3dov\u00fd bod\u201c.<\/p>\n<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/7ad84b571a8025582fdbc3db956cb3b0.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Plo\u0161iny Unicode. Jak je vid\u011bt, v\u011bt\u0161ina (plo\u0161iny 4 a\u017e 13) st\u00e1le nen\u00ed vyu\u017eita.<\/i><\/p>\n<p>Nejpozoruhodn\u011bj\u0161\u00ed je, \u017ee ve\u0161ker\u00e1 hlavn\u00ed \"du\u017eina\" le\u017e\u00ed na nult\u00e9 rovin\u011b, naz\u00fdv\u00e1 se \"<em>Basic Multilingual Plane<\/em>\". Pokud \u0159et\u011bzec obsahuje text v n\u011bkter\u00e9m z modern\u00edch jazyk\u016f (v\u010detn\u011b \u010d\u00edn\u0161tiny), z t\u00e9to roviny se nedostanete. Ale nem\u016f\u017eete tak\u00e9 odst\u0159ihnout zbytek Unicode \u2014 nap\u0159\u00edklad emoji se v\u011bt\u0161inou nach\u00e1zej\u00ed na konci dal\u0161\u00ed roviny, \"<em>Supplementary Multilingual Plane<\/em>\" (ta se rozprost\u00edr\u00e1 od <code><b>0x10000<\/b><\/code> do <code><b>0x1FFFF<\/b><\/code>). Proto UTF-16 postupuje takto: v\u0161echny znaky, kter\u00e9 spadaj\u00ed do <em>Basic Multilingual Plane<\/em>, jsou k\u00f3dov\u00e1ny \u201etak jak jsou\u201c, odpov\u00eddaj\u00edc\u00edm dvoubajtov\u00fdm \u010d\u00edslem. Nicm\u00e9n\u011b \u010d\u00e1st \u010d\u00edsel v tomto rozsahu v\u016fbec neozna\u010duje konkr\u00e9tn\u00ed znaky, ale nazna\u010duje, \u017ee po tomto p\u00e1ru bajt\u016f je pot\u0159eba zv\u00e1\u017eit je\u0161t\u011b jeden \u2014 zkombinov\u00e1n\u00edm hodnot t\u011bchto \u010dty\u0159 bajt\u016f dohromady dostaneme \u010d\u00edslo, kter\u00e9 pokr\u00fdv\u00e1 cel\u00fd povolen\u00fd rozsah Unicode. Tento zp\u016fsob reprezentace se naz\u00fdv\u00e1 \u201esurrog\u00e1tn\u00ed p\u00e1ry\u201c \u2014 mo\u017en\u00e1 jste o nich sly\u0161eli.<\/p>\n<p>UTF-16 tak vy\u017eaduje dva nebo (ve velmi vz\u00e1cn\u00fdch p\u0159\u00edpadech) \u010dty\u0159i bajty na jednu \u201ek\u00f3dovou bod\u201c. To je lep\u0161\u00ed ne\u017e neust\u00e1le pou\u017e\u00edvat \u010dty\u0159i bajty, ale latinka (a jin\u00e9 ASCII znaky) p\u0159i tomto k\u00f3dov\u00e1n\u00ed spot\u0159ebov\u00e1v\u00e1 polovinu m\u00edsta na nuly. UTF-8 m\u00e1 tento probl\u00e9m napravit: ASCII v n\u011bm z\u016fst\u00e1v\u00e1 na jednom bajtu; k\u00f3dy od <code><b>0x80<\/b><\/code> do <code><b>0x7FF<\/b><\/code> \u2014 dvou bajt\u016f; od <code><b>0x800<\/b><\/code> do <code><b>0xFFFF<\/b><\/code> \u2014 t\u0159\u00ed, a od <code><b>0x10000<\/b><\/code> do <code><b>0x10FFFF<\/b><\/code> \u2014 \u010dty\u0159. Na jedn\u00e9 stran\u011b se latince da\u0159\u00ed dob\u0159e: vrac\u00ed se kompatibilita s ASCII, a tak\u00e9 rozd\u011blen\u00ed je v\u00edce rovnom\u011brn\u011b \u201erozlo\u017eeno\u201c od 1 do 4 bajt\u016f. Ale abecedy, kter\u00e9 nejsou latinsk\u00e9, bohu\u017eel nevyhr\u00e1vaj\u00ed ve srovn\u00e1n\u00ed s UTF-16, a mnoh\u00e9 nyn\u00ed vy\u017eaduj\u00ed dokonce t\u0159i bajty m\u00edsto dvou \u2014 rozsah pokryt\u00fd dvoubajtov\u00fdm z\u00e1pisem se z\u00fa\u017eil 32kr\u00e1t, od <code><b>0xFFFF<\/b><\/code> do <code><b>0x7FF<\/b><\/code>, a do n\u00ed u\u017e nespad\u00e1 ani \u010d\u00edn\u0161tina, ani nap\u0159\u00edklad gruz\u00edn\u0161tina. Cyrilici a je\u0161t\u011b p\u011bti abeced\u00e1m \u2014 hur\u00e1 \u2014 se poda\u0159ilo, 2 bajty na znak.<\/p>\n<p>Pro\u010d to tak je? Poj\u010fme se pod\u00edvat, jak UTF-8 p\u0159edstavuje k\u00f3dy znak\u016f:<br \/>\n<img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/4ef4fe9e949cd75295c45c053dbca6d3.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nP\u0159\u00edmo pro reprezentaci \u010d\u00edsel se zde pou\u017e\u00edvaj\u00ed bity ozna\u010den\u00e9 symbolem <code><b>x<\/b><\/code>. Je vid\u011bt, \u017ee v dvoubajtov\u00e9m z\u00e1pisu je takov\u00fdch bit\u016f pouze 11 (ze 16). Vedouc\u00ed bity zde maj\u00ed pouze slu\u017eebn\u00ed funkci. V p\u0159\u00edpad\u011b \u010dty\u0159bajtov\u00e9ho z\u00e1pisu je dokonce pro \u010d\u00edslo k\u00f3dov\u00e9ho bodu vyhrazeno 21 bit\u016f ze 32 \u2014 zd\u00e1lo by se, \u017ee by sta\u010dily i t\u0159i bajty (kter\u00e9 d\u00e1vaj\u00ed celkem 24 bit\u016f), ale slu\u017eebn\u00ed markery zab\u00edraj\u00ed p\u0159\u00edli\u0161 mnoho.<\/p>\n<p>Je to \u0161patn\u011b? Opraviteln\u011b, ne p\u0159\u00edli\u0161. Na jedn\u00e9 stran\u011b \u2014 pokud n\u00e1m opravdu z\u00e1le\u017e\u00ed na zabran\u00e9m prostoru, m\u00e1me algoritmy komprese, kter\u00e9 snadno odstran\u00ed ve\u0161kerou zbyte\u010dnou entropii a nadbyte\u010dnost. Na druh\u00e9 \u2014 c\u00edlem Unicode bylo poskytnout co nejuniverz\u00e1ln\u011bj\u0161\u00ed k\u00f3dov\u00e1n\u00ed. Nap\u0159\u00edklad \u0159\u00e1dek zak\u00f3dovan\u00fd v UTF-8 m\u016f\u017eeme sv\u011b\u0159it k\u00f3du, kter\u00fd d\u0159\u00edve pracoval pouze s ASCII, a nemus\u00edme se b\u00e1t, \u017ee tam uvid\u00ed znak z ASCII rozsahu, kter\u00fd tam ve skute\u010dnosti nen\u00ed (v\u017edy\u0165 v UTF-8 jsou v\u0161echny bajty za\u010d\u00ednaj\u00edc\u00ed nulov\u00fdm bitem pr\u00e1v\u011b ASCII). A pokud bychom se n\u00e1hle cht\u011bli zbavit mal\u00e9ho oc\u00e1sku velk\u00e9ho \u0159et\u011bzce, ani\u017e bychom ho dek\u00f3dovali od za\u010d\u00e1tku (nebo obnovili \u010d\u00e1st informac\u00ed po po\u0161kozen\u00e9 \u010d\u00e1sti) \u2014 nen\u00ed t\u011b\u017ek\u00e9 naj\u00edt to posunut\u00ed, kde za\u010d\u00edn\u00e1 n\u011bjak\u00fd znak (sta\u010d\u00ed p\u0159esko\u010dit bajty, kter\u00e9 maj\u00ed bitov\u00fd prefix <code><b>10<\/b><\/code>).<\/p>\n<h2>Pro\u010d tedy vym\u00fd\u0161let n\u011bco nov\u00e9ho?<\/h2>\n<p>\nSoub\u011b\u017en\u011b se ob\u010das vyskytuj\u00ed situace, kdy algoritmy komprese jako deflate nejsou \u0161patn\u011b pou\u017eiteln\u00e9, ale je t\u0159eba dos\u00e1hnout kompaktn\u00edho ulo\u017een\u00ed \u0159et\u011bzc\u016f. Osobn\u011b jsem se s takov\u00fdm \u00fakolem setkal p\u0159i \u00favah\u00e1ch o konstrukci <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Radix_tree\">komprimovan\u00e9ho prefixov\u00e9ho stromu<\/a><\/noindex> pro velk\u00fd slovn\u00edk zahrnuj\u00edc\u00ed slova v libovoln\u00fdch jazyc\u00edch. Na jedn\u00e9 stran\u011b \u2013 ka\u017ed\u00e9 slovo je velmi kr\u00e1tk\u00e9, tak\u017ee jeho komprese bude neefektivn\u00ed. Na druh\u00e9 stran\u011b \u2013 implementace stromu, kterou jsem zva\u017eoval, byla navr\u017eena tak, \u017ee ka\u017ed\u00fd byte ulo\u017een\u00e9ho \u0159et\u011bzce generoval samostatn\u00fd uzel stromu, tak\u017ee minimalizace jejich po\u010dtu byla velmi u\u017eite\u010dn\u00e1. V m\u00e9 knihovn\u011b <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/deNULL\/Az.js\">Az.js<\/a><\/noindex> (stejn\u011b jako v <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/kmike\/pymorphy2\">pymorphy2<\/a><\/noindex>, na kter\u00e9m je zalo\u017eena) se podobn\u00fd probl\u00e9m \u0159e\u0161\u00ed jednodu\u0161e \u2013 \u0159et\u011bzce ulo\u017een\u00e9 v <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Deterministic_acyclic_finite_state_automaton\">DAWG<\/a><\/noindex>-slovn\u00edku se zde uchov\u00e1vaj\u00ed v <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/deNULL\/Az.js\/blob\/master\/src\/az.dawg.js\">dobr\u00e9m star\u00e9m CP1251<\/a><\/noindex>. Jak v\u0161ak snadno pochop\u00edte, to funguje pouze pro omezenou abecedu \u2013 \u0159et\u011bzec v \u010d\u00edn\u0161tin\u011b u\u017e do takov\u00e9ho slovn\u00edku nelze vlo\u017eit.<\/p>\n<p>Zvl\u00e1\u0161\u0165 bych cht\u011bl zd\u016fraznit je\u0161t\u011b jedno nep\u0159\u00edjemn\u00e9 \u00faskal\u00ed, kter\u00e9 se objevuje p\u0159i pou\u017eit\u00ed UTF-8 v takov\u00e9 datov\u00e9 struktu\u0159e. Na obr\u00e1zku v\u00fd\u0161e je vid\u011bt, \u017ee p\u0159i z\u00e1pisu symbolu ve form\u011b dvou byt\u016f nejsou bity, kter\u00e9 se vztahuj\u00ed k jeho \u010d\u00edslu, za sebou, ale jsou p\u0159eru\u0161eny dvojic\u00ed bit\u016f <code><b>10<\/b><\/code> uprost\u0159ed: <code><b>110xxxxx 10xxxxxx<\/b><\/code>. V d\u016fsledku toho, kdy\u017e v k\u00f3du symbolu p\u0159epln\u00edme mlad\u0161\u00edch 6 bit\u016f druh\u00e9ho bytu (tj. doch\u00e1z\u00ed k p\u0159echodu <code><b>10111111<\/b><\/code> \u2192 <code><b>10000000<\/b><\/code>), zm\u011bn\u00ed se i prvn\u00ed byte. Tak se p\u00edsmeno \u201e\u043f\u201c ozna\u010duje byty <code><b>0xD0 0xBF<\/b><\/code>, a to n\u00e1sledn\u00e9 \u201e\u0440\u201c \u2013 u\u017e <code><b>0xD1&nbsp;0x80<\/b><\/code>. V prefixov\u00e9m stromu to vede k rozd\u011blen\u00ed rodi\u010dovsk\u00e9ho uzlu na dva \u2013 jeden pro prefix <code><b>0xD0<\/b><\/code>, a druh\u00fd pro <code><b>0xD1<\/b><\/code> (i kdy\u017e by cel\u00e1 cyrilika mohla b\u00fdt k\u00f3dov\u00e1na pouze druh\u00fdm bytem).<\/p>\n<h2>Co se mi poda\u0159ilo<\/h2>\n<p>\nKdy\u017e jsem se s t\u00edmto \u00fakolem setkal, rozhodl jsem se procvi\u010dit si hru s bity a z\u00e1rove\u0148 se bl\u00ed\u017ee sezn\u00e1mit se strukturou Unicode jako takov\u00e9. V\u00fdsledkem byl k\u00f3dovac\u00ed form\u00e1t UTF-C (\u201eC\u201c znamen\u00e1 <em>kompaktn\u00ed<\/em>), kter\u00fd spot\u0159ebov\u00e1v\u00e1 maxim\u00e1ln\u011b 3 byty na jeden k\u00f3dov\u00fd bod, a velmi \u010dasto umo\u017e\u0148uje u\u0161et\u0159it pouze <strong>jeden dal\u0161\u00ed byte na cel\u00fd k\u00f3dovan\u00fd \u0159et\u011bzec<\/strong>. To m\u00e1 za n\u00e1sledek, \u017ee v mnoha ne-ASCII abeced\u00e1ch je toto k\u00f3dov\u00e1n\u00ed <strong>o 30-60% kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8<\/strong>.<\/p>\n<p>P\u0159ipravil jsem p\u0159\u00edklady implementace algoritm\u016f k\u00f3dov\u00e1n\u00ed a dek\u00f3dov\u00e1n\u00ed ve form\u011b <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/deNULL\/utf-c\">knihoven v JavaScriptu a Go<\/a><\/noindex>, m\u016f\u017eete je voln\u011b pou\u017e\u00edvat ve sv\u00e9m k\u00f3du. Ale p\u0159esto zd\u016fraz\u0148uji, \u017ee v ur\u010dit\u00e9m smyslu z\u016fst\u00e1v\u00e1 tento form\u00e1t \u201ekola\u201c, a nedoporu\u010duji ho pou\u017e\u00edvat <strong>bez pov\u011bdom\u00ed o tom, pro\u010d ho pot\u0159ebujete<\/strong>. Je to p\u0159ece v\u00edce experiment ne\u017e v\u00e1\u017en\u00e9 \u201evylep\u0161en\u00ed UTF-8\u201c. Nicm\u00e9n\u011b je k\u00f3d naps\u00e1n pe\u010dliv\u011b, stru\u010dn\u011b, s velk\u00fdm mno\u017estv\u00edm koment\u00e1\u0159\u016f a pokryt\u00edm testy.<\/p>\n<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/e31978174449cd7de5d8371aaeb9ab2e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>V\u00fdsledek test\u016f a srovn\u00e1n\u00ed s UTF-8<\/i><\/p>\n<p>Tak\u00e9 jsem vytvo\u0159il <noindex><a rel=\"nofollow\" href=\"https:\/\/denull.github.io\/utf-c\/\">demo str\u00e1nku<\/a><\/noindex>, kde si m\u016f\u017eete vyzkou\u0161et fungov\u00e1n\u00ed algoritmu, a d\u00e1le v\u00e1m podrobn\u011bji pop\u00ed\u0161u jeho principy a proces v\u00fdvoje.<\/p>\n<h2>Odstra\u0148ujeme nadbyte\u010dn\u00e9 bity<\/h2>\n<p>\nJako z\u00e1klad jsem vzal samoz\u0159ejm\u011b UTF-8. Prvn\u00ed a nejz\u0159ejm\u011bj\u0161\u00ed v\u011bc\u00ed, kterou m\u016f\u017eete zm\u011bnit, je sn\u00ed\u017eit po\u010det ovl\u00e1dac\u00edch bit\u016f v ka\u017ed\u00e9m bajtu. Nap\u0159\u00edklad, prvn\u00ed bajt v UTF-8 v\u017edy za\u010d\u00edn\u00e1 bu\u010f <code><b>0<\/b><\/code>, nebo s <code><b>11<\/b><\/code> \u2014 a prefix <code><b>10<\/b><\/code> je pouze u n\u00e1sleduj\u00edc\u00edch bajt\u016f. Nahrad\u00edme prefix <code><b>11<\/b><\/code> na <code><b>1<\/b><\/code>, a u n\u00e1sleduj\u00edc\u00edch bajt\u016f \u00fapln\u011b odstran\u00edme prefixy. Co se stane?<\/p>\n<p><code><b>0xxxxxxx<\/b><\/code> \u2014 1 bajt <br \/>\n<code><b>10xxxxxx xxxxxxxx<\/b><\/code> \u2014 2 bajty <br \/>\n<code><b>110xxxxx xxxxxxxx xxxxxxxx<\/b><\/code> \u2014 3 bajty<\/p>\n<p>Stop, kde je \u010dty\u0159bajtov\u00e1 z\u00e1pis? Ta se stala zbyte\u010dnou \u2014 p\u0159i z\u00e1pisu t\u0159emi bajty m\u00e1me nyn\u00ed k dispozici 21 bit\u016f a to sta\u010d\u00ed na v\u0161echna \u010d\u00edsla a\u017e <code><b>0x10FFFF<\/b><\/code>.<\/p>\n<p>\u010c\u00edm jsme tu ob\u011btovali? Nejd\u016fle\u017eit\u011bj\u0161\u00ed je zji\u0161t\u011bn\u00ed hranic symbol\u016f z libovoln\u00e9ho m\u00edsta v bufferu. Nem\u016f\u017eeme se prost\u011b vybrat n\u00e1hodn\u00fd bajt a od n\u011bj naj\u00edt za\u010d\u00e1tek dal\u0161\u00edho symbolu. To je omezen\u00ed na\u0161eho form\u00e1tu, ale v praxi se tou pot\u0159ebou setk\u00e1v\u00e1me z\u0159\u00eddka. Obvykle m\u016f\u017eeme prob\u011bhnout buffer od za\u010d\u00e1tku (zejm\u00e9na kdy\u017e jde o kr\u00e1tk\u00e9 \u0159et\u011bzce).<\/p>\n<p>Situace s pokryt\u00edm jazyk\u016f 2 bajty se tak\u00e9 zlep\u0161ila: nyn\u00ed dvoubajtov\u00fd form\u00e1t poskytuje rozsah 14 bit\u016f, co\u017e odpov\u00edd\u00e1 k\u00f3d\u016fm a\u017e <code><b>0x3FFF<\/b><\/code>. \u010c\u00ed\u0148an\u00e9 to maj\u00ed t\u011b\u017ek\u00e9 (jejich znaky le\u017e\u00ed p\u0159ev\u00e1\u017en\u011b v rozmez\u00ed od <code><b>0x4E00<\/b><\/code> do <code><b>0x9FFF<\/b><\/code>), ale Gruz\u00ednci a mnoho dal\u0161\u00edch n\u00e1rod\u016f maj\u00ed nyn\u00ed v\u00edce radosti \u2014 jejich jazyky se vejdou do 2 byt\u016f na symbol.<\/p>\n<h2>Zav\u00e1d\u00edme stav k\u00f3dova\u010de<\/h2>\n<p>\nNyn\u00ed si promysl\u00edme vlastnosti samotn\u00fdch \u0159et\u011bzc\u016f. Ve slovn\u00edku se v\u011bt\u0161inou nach\u00e1zej\u00ed slova napsan\u00e1 symboly jedn\u00e9 abecedy, a to plat\u00ed i pro mnoho dal\u0161\u00edch text\u016f. Bylo by dobr\u00e9 jednou zadat tuto abecedu a pot\u00e9 uv\u00e1d\u011bt pouze \u010d\u00edslo p\u00edsmene uvnit\u0159 n\u00ed. Pod\u00edvejme se, jestli n\u00e1m uspo\u0159\u00e1d\u00e1n\u00ed symbol\u016f v tabulce Unicode pom\u016f\u017ee.<\/p>\n<p>Jak bylo uvedeno v\u00fd\u0161e, Unicode je rozd\u011blen na <em>plochy<\/em> po 65536 k\u00f3d\u016f ka\u017ed\u00fd. Ale to nen\u00ed p\u0159\u00edli\u0161 u\u017eite\u010dn\u00e9 d\u011blen\u00ed (jak ji\u017e bylo \u0159e\u010deno, v\u011bt\u0161inou se nach\u00e1z\u00edme v nulov\u00e9 rovin\u011b). Zaj\u00edmav\u011bj\u0161\u00ed je d\u011blen\u00ed na <em>bloky.<\/em> Tyto rozsahy ji\u017e nemaj\u00ed pevnou d\u00e9lku a nesou v\u011bt\u0161\u00ed smysl \u2014 obvykle ka\u017ed\u00fd sdru\u017euje znaky jednoho abecedy.<\/p>\n<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/61ea5e859d7e6d9c75e977a3579ff28f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Blok obsahuj\u00edc\u00ed znaky beng\u00e1lsk\u00e9 abecedy. Bohu\u017eel, z historick\u00fdch d\u016fvod\u016f, je to p\u0159\u00edklad ne p\u0159\u00edli\u0161 hust\u00e9ho balen\u00ed \u2014 96 znak\u016f je chaoticky rozpt\u00fdleno po 128 k\u00f3dov\u00fdch bodech bloku.<\/i><\/p>\n<p>Za\u010d\u00e1tky blok\u016f a jejich velikosti jsou v\u017edy n\u00e1sobky 16 \u2014 je to ud\u011bl\u00e1no prost\u011b pro pohodl\u00ed. Krom\u011b toho mnoho blok\u016f za\u010d\u00edn\u00e1 a kon\u010d\u00ed na hodnot\u00e1ch, kter\u00e9 jsou n\u00e1sobky 128 nebo dokonce 256 \u2014 nap\u0159\u00edklad, hlavn\u00ed cyrilice zab\u00edr\u00e1 256 byt\u016f od <code><b>0x0400<\/b><\/code> do <code><b>0x04FF<\/b><\/code>. To je docela pohodln\u00e9: pokud jednou ulo\u017e\u00edme prefix <code><b>0x04<\/b><\/code>, pak jak\u00fdkoliv cyrilsk\u00fd znak lze zapsat jedn\u00edm bytem. Pravda, tak ztrat\u00edme mo\u017enost vr\u00e1tit se k ASCII (a k jak\u00fdmkoliv jin\u00fdm znak\u016fm v\u016fbec). Proto to d\u011bl\u00e1me takto:<\/p>\n<ol>\n<li>Dva byty <code><b>10yyyyyy yxxxxxxx<\/b><\/code> nejen\u017ee ozna\u010duj\u00ed znak s \u010d\u00edslem <code><b>yyyyyy yxxxxxxx<\/b><\/code>, ale tak\u00e9 m\u011bn\u00ed <em>aktu\u00e1ln\u00ed abecedu<\/em> na <code><b>yyyyyy y0000000<\/b><\/code> tj. zapamatujeme si v\u0161echny bity, krom\u011b nejni\u017e\u0161\u00edch <strong>7 bit<\/strong>);<\/li>\n<li>Jeden byt <code><b>0xxxxxxx<\/b><\/code> to je znak aktu\u00e1ln\u00ed abecedy. Jeho je pot\u0159eba jednodu\u0161e se\u010d\u00edst s t\u00edm posunem, kter\u00fd jsme si zapamatovali v kroku 1. Dokud jsme abecedu nezm\u011bnili, posun je roven nule, tak\u017ee jsme zachovali kompatibilitu s ASCII.<\/li>\n<\/ol>\n<p>\nAnalogicky pro k\u00f3dy, kter\u00e9 vy\u017eaduj\u00ed 3 byty:<\/p>\n<ol>\n<li>T\u0159i byty <code><b>110yyyyy yxxxxxxx xxxxxxxx<\/b><\/code> ozna\u010duj\u00ed znak s \u010d\u00edslem <code><b>yyyyyy yxxxxxxx xxxxxxxx<\/b><\/code>, m\u011bn\u00ed <em>aktu\u00e1ln\u00ed abecedu<\/em> na <code><b>yyyyyy y0000000 00000000<\/b><\/code> zapamatovali jsme si v\u0161echno krom\u011b nejni\u017e\u0161\u00edch <strong>15 bit<\/strong>), a nastavuj\u00ed p\u0159\u00edznak, \u017ee jsme nyn\u00ed v <em>dlouh\u00e9m<\/em> re\u017eimu (p\u0159i zm\u011bn\u011b abecedy zp\u011bt na dvoubajtov\u00fd tento p\u0159\u00edznak resetujeme);<\/li>\n<li>Dva byty <code><b>0xxxxxxx xxxxxxxx<\/b><\/code> v dlouh\u00e9m re\u017eimu to je znak aktu\u00e1ln\u00ed abecedy. Analogicky, se\u010d\u00edt\u00e1me ho s posunem z kroku 1. Cel\u00fd rozd\u00edl je jen v tom, \u017ee nyn\u00ed \u010dteme po dvou bytech (proto\u017ee jsme p\u0159e\u0161li do takov\u00e9ho re\u017eimu).<\/li>\n<\/ol>\n<p>\nZn\u00ed to dob\u0159e: nyn\u00ed, dokud pot\u0159ebujeme k\u00f3dovat znaky z jednoho a t\u00e9ho\u017e 7-bitov\u00e9ho rozsahu Unicode, utr\u00e1c\u00edme 1 p\u0159ebyte\u010dn\u00fd byt na za\u010d\u00e1tku a jen po jednom bytu na ka\u017ed\u00fd znak.<\/p>\n<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/084d636a25dccdaa9f5a6eb5233c8e99.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Pr\u00e1ce jedn\u00e9 z ran\u00fdch verz\u00ed. Ji\u017e \u010dasto obch\u00e1z\u00ed UTF-8, ale st\u00e1le je co zlep\u0161ovat.<\/i><\/p>\n<p>Co se stalo h\u016f\u0159e? P\u0159edev\u0161\u00edm, m\u00e1me stav, a to konkr\u00e9tn\u011b <em>posun aktu\u00e1ln\u00ed abecedy<\/em> a p\u0159\u00edznak <em>dlouh\u00e9ho re\u017eimu<\/em>. To n\u00e1s nav\u00edc omezuje: nyn\u00ed mohou b\u00fdt stejn\u00e9 znaky zak\u00f3dov\u00e1ny odli\u0161n\u011b v r\u016fzn\u00fdch kontextech. Hled\u00e1n\u00ed pod\u0159et\u011bzc\u016f, nap\u0159\u00edklad, bude muset br\u00e1t toto v \u00favahu m\u00edsto pouh\u00e9ho porovn\u00e1v\u00e1n\u00ed byt\u016f. Za druh\u00e9, jakmile jsme zm\u011bnili abecedu, do\u0161lo k probl\u00e9m\u016fm s k\u00f3dov\u00e1n\u00edm ASCII znak\u016f (a to zahrnuje nejen latinskou abecedu, ale i z\u00e1kladn\u00ed interpunkci, v\u010detn\u011b mezer) \u2014 ty vy\u017eaduj\u00ed zm\u011bnu abecedy do 0, tedy op\u011bt zbyte\u010dn\u00fd byte (a pot\u00e9 je\u0161t\u011b jeden, abychom se vr\u00e1tili k na\u0161\u00ed hlavn\u00ed).<\/p>\n<h2>Jedna abeceda je dobr\u00e1, dv\u011b jsou lep\u0161\u00ed.<\/h2>\n<p>\nZkus\u00edme trochu zm\u011bnit na\u0161e bitov\u00e9 prefixy t\u00edm, \u017ee k t\u0159em v\u00fd\u0161e zm\u00edn\u011bn\u00fdm p\u0159id\u00e1me je\u0161t\u011b jeden:<\/p>\n<p><code><b>0xxxxxxx<\/b><\/code> \u2014 1 byte v b\u011b\u017en\u00e9m re\u017eimu, 2 v dlouh\u00e9m. <br \/>\n<code><b>11xxxxxx<\/b><\/code> \u2014 1 bajt <br \/>\n<code><b>100xxxxx xxxxxxxx<\/b><\/code> \u2014 2 bajty <br \/>\n<code><b>101xxxxx xxxxxxxx xxxxxxxx<\/b><\/code> \u2014 3 bajty<\/p>\n<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/fa1eca1adb80b15a4cf4f60f57a09c6c.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNyn\u00ed v dvoubajtov\u00e9m z\u00e1pisu k dispozici o jeden bit m\u00e9n\u011b \u2014 k\u00f3dov\u00e9 body se vejdou a\u017e do <code><b>0x1FFF<\/b><\/code>, a ne <code><b>0x3FFF<\/b><\/code>. I kdy\u017e je to st\u00e1le v\u00fdrazn\u011b v\u00edc ne\u017e u dvoubajtov\u00fdch k\u00f3d\u016f UTF-8, v\u011bt\u0161ina b\u011b\u017en\u00fdch jazyk\u016f se st\u00e1le vejde, nejv\u00fdrazn\u011bj\u0161\u00ed ztr\u00e1ta \u2014 vypadla <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A5%D0%B8%D1%80%D0%B0%D0%B3%D0%B0%D0%BD%D0%B0\">hiragana<\/a><\/noindex> a <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9A%D0%B0%D1%82%D0%B0%D0%BA%D0%B0%D0%BD%D0%B0\">katakana<\/a><\/noindex>, Japonci jsou smutn\u00ed.<\/p>\n<p>Co to je za nov\u00fd k\u00f3d <code><b>11xxxxxx<\/b><\/code>? \u042d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u00ab\u0437\u0430\u0433\u0430\u0448\u043d\u0438\u043a\u00bb \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c \u0432 64 \u0441\u0438\u043c\u0432\u043e\u043b\u0430, \u043e\u043d \u0434\u043e\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043d\u0430\u0448 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0430\u043b\u0444\u0430\u0432\u0438\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0430\u0437\u0432\u0430\u043b \u0435\u0433\u043e \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c (<em>auxiliary<\/em>) abecedou. Kdy\u017e p\u0159epneme sou\u010dasnou abecedu, kus star\u00e9 abecedy se stane pomocnou. Nap\u0159\u00edklad, p\u0159e\u0161li jsme z ASCII na cyrilici \u2014 v \u201ez\u00e1loze\u201c je nyn\u00ed 64 znak\u016f, kter\u00e9 obsahuj\u00ed <strong>latinku, \u010d\u00edsla, mezeru a \u010d\u00e1rku<\/strong> (nej\u010dast\u011bj\u0161\u00ed prvky v ne-ASCII textech). P\u0159epnuli jsme se zp\u011bt na ASCII \u2014 a pomocnou abecedou se stane hlavn\u00ed \u010d\u00e1st cyrilice.<\/p>\n<p>D\u00edky p\u0159\u00edstupu k dv\u011bma abeced\u00e1m m\u016f\u017eeme zpracovat velk\u00e9 mno\u017estv\u00ed text\u016f s minim\u00e1ln\u00edmi n\u00e1klady na p\u0159ep\u00edn\u00e1n\u00ed abeced (interpunkce v\u011bt\u0161inou povede k n\u00e1vratu do ASCII, ale pot\u00e9 mnoh\u00e9 ne-ASCII znaky ji\u017e vyjmeme z dopl\u0148kov\u00e9 abecedy, bez op\u011btovn\u00e9ho p\u0159ep\u00edn\u00e1n\u00ed).<\/p>\n<p>Bonus: ozna\u010den\u00edm dopl\u0148kov\u00e9 abecedy prefixem <code><b>11xxxxxx<\/b><\/code> a v\u00fdb\u011brem jej\u00edho po\u010d\u00e1te\u010dn\u00edho posunut\u00ed rovn\u00e9ho <code><b>0xC0<\/b><\/code>, dostaneme \u010d\u00e1ste\u010dnou kompatibilitu s CP1252. Jin\u00fdmi slovy, mnoh\u00e9 (ale ne v\u0161echny) z\u00e1padoevropsk\u00e9 texty zak\u00f3dovan\u00e9 v CP1252 budou vypadat stejn\u011b i v UTF-C.<\/p>\n<p>Zde v\u0161ak vznik\u00e1 probl\u00e9m: jak z\u00edskat pomocnou abecedu z hlavn\u00ed? M\u016f\u017eeme nechat stejn\u00e9 posunut\u00ed, ale \u2014 bohu\u017eel \u2014 zde u\u017e struktura Unicode hraje proti n\u00e1m. Velmi \u010dasto se hlavn\u00ed \u010d\u00e1st abecedy nenach\u00e1z\u00ed na za\u010d\u00e1tku bloku (nap\u0159\u00edklad rusk\u00e9 velk\u00e9 p\u00edsmeno \u201eA\u201c m\u00e1 k\u00f3d <code>0x04<b>10<\/b><\/code>, zat\u00edmco Kyrilick\u00e1 blok za\u010d\u00edn\u00e1 na <code>0x04<b>00<\/b><\/code>). Takto, vezmeme-li do \u201e\u00falo\u017ei\u0161t\u011b\u201c prvn\u00edch 64 znak\u016f, m\u016f\u017eeme potenci\u00e1ln\u011b ztratit p\u0159\u00edstup k zadn\u00ed \u010d\u00e1sti abecedy.<\/p>\n<p>Abych tento probl\u00e9m vy\u0159e\u0161il, ru\u010dn\u011b jsem proch\u00e1zel n\u011bkter\u00e9 bloky odpov\u00eddaj\u00edc\u00ed r\u016fzn\u00fdm jazyk\u016fm a uvedl jsem pro n\u011b posunut\u00ed pomocn\u00e9ho abecedy uvnit\u0159 hlavn\u00edho. Latinku jsem, z v\u00fdjimky, zcela p\u0159euspo\u0159\u00e1dal obdobn\u011b jako base64.<\/p>\n<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/d191a3bb17403e99d506dbd008b63159.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>Fin\u00e1ln\u00ed \u00fapravy<\/h2>\n<p>\nPoj\u010fme na z\u00e1v\u011br pop\u0159em\u00fd\u0161let, kde je\u0161t\u011b m\u016f\u017eeme n\u011bco vylep\u0161it.<\/p>\n<p>V\u0161imn\u011bme si, \u017ee form\u00e1t <code><b>101xxxxx xxxxxxxx xxxxxxxx<\/b><\/code> umo\u017e\u0148uje k\u00f3dovat \u010d\u00edsla a\u017e do <code><b>0x1FFFFF<\/b><\/code>, ale Unicode kon\u010d\u00ed d\u0159\u00edve, na <code><b>0x10FFFF<\/b><\/code>. Jin\u00fdmi slovy, posledn\u00ed k\u00f3dov\u00fd bod bude vyj\u00e1d\u0159en jako <code><b>10110000 11111111 11111111<\/b><\/code>. Tedy m\u016f\u017eeme \u0159\u00edci, \u017ee pokud prvn\u00ed bajt m\u00e1 podobu <code><b>1011xxxx<\/b><\/code> (kde <code><b>xxxx<\/b><\/code> v\u00edce ne\u017e 0), pak znamen\u00e1 n\u011bco jin\u00e9ho. Nap\u0159\u00edklad bychom mohli p\u0159idat je\u0161t\u011b 15 znak\u016f, trvale dostupn\u00fdch pro k\u00f3dov\u00e1n\u00ed jedn\u00edm bajtem, ale rozhodl jsem se jednat jinak.<\/p>\n<p>Pod\u00edvejme se na ty bloky Unicode, kter\u00e9 v sou\u010dasnosti vy\u017eaduj\u00ed t\u0159i bajty. V\u011bt\u0161inou, jak ji\u017e bylo zm\u00edn\u011bno, jsou to \u010d\u00ednsk\u00e9 znaky \u2013 ale s nimi se t\u011b\u017eko n\u011bco d\u011bl\u00e1, je jich 21 tis\u00edc. Ale tak\u00e9 sem spad\u00e1 hiragana a katakana \u2013 a t\u011bch u\u017e nen\u00ed tolik, m\u00e9n\u011b ne\u017e dv\u011b stovky. A pokud jsme vzpomn\u011bli na Japonce \u2013 tam le\u017e\u00ed tak\u00e9 emoji (ve skute\u010dnosti jsou rozpt\u00fdlen\u00e1 po cel\u00e9m Unicode, ale hlavn\u00ed bloky jsou v rozsahu <code><b>0x1F300<\/b><\/code> \u2013 <code><b>0x1FBFF<\/b><\/code>). Pokud si uv\u011bdom\u00edme, \u017ee nyn\u00ed existuj\u00ed emoji, kter\u00e9 se skl\u00e1daj\u00ed z n\u011bkolika k\u00f3dov\u00fdch bod\u016f (nap\u0159\u00edklad emoji \u200d\u200d\u200d<noindex><a rel=\"nofollow\" href=\"https:\/\/emojipedia.org\/family-woman-woman-girl-boy\/\"><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/76cd12423b241cc316af80f03be4348f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex> se skl\u00e1d\u00e1 z 7 k\u00f3d\u016f!), pak je opravdu \u0161koda str\u00e1vit t\u0159i bajty na ka\u017ed\u00e9 (7\u00d73 = 21 bajt\u016f za jeden znak, to je \u0161\u00edlenstv\u00ed).<\/p>\n<p>Proto vyb\u00edr\u00e1me n\u011bkolik vybran\u00fdch rozsah\u016f odpov\u00eddaj\u00edc\u00edch emoji, hiragan\u011b a katakan\u011b, p\u0159enumerov\u00e1v\u00e1me je do jednoho nep\u0159etr\u017eit\u00e9ho seznamu a k\u00f3dujeme jako dva bajty m\u00edsto t\u0159\u00ed:<\/p>\n<p><code><b>1011xxxx xxxxxxxx<\/b><\/code> <\/p>\n<p>Skv\u011bl\u00e9: v\u00fd\u0161e zm\u00edn\u011bn\u00e9 emoji \u200d\u200d\u200d<noindex><a rel=\"nofollow\" href=\"https:\/\/emojipedia.org\/family-woman-woman-girl-boy\/\"><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/b9900c78dda5d8e596e3751021b4d35d.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex>, slo\u017een\u00e9 ze 7 k\u00f3dov\u00fdch bod\u016f, v UTF-8 zab\u00edr\u00e1 25 bajt\u016f, ale my jsme ho ve\u0161li do <strong>14<\/strong> p\u0159esn\u011b dva bajty na ka\u017ed\u00fd k\u00f3dov\u00fd bod). Mimochodem, Habr odm\u00edtl jej zpracovat (jak ve star\u00e9m, tak v nov\u00e9m editoru), tak\u017ee jsem musel vlo\u017eit jeho obr\u00e1zkem.<\/p>\n<p>Zkus\u00edme opravit je\u0161t\u011b jeden probl\u00e9m. Jak v\u00edme, hlavn\u00ed abeceda je vlastn\u011b <strong>vy\u0161\u0161\u00edch 6 bit\u016f<\/strong>, kter\u00e9 m\u00e1me v pam\u011bti, a p\u0159ilep\u00edme k\u00f3d k ka\u017ed\u00e9mu dal\u0161\u00edmu dek\u00f3dovan\u00e9mu znaku. Co se t\u00fd\u010de \u010d\u00ednsk\u00fdch znak\u016f, kter\u00e9 jsou v bloku <code><b>0x4E00<\/b><\/code> \u2013 <code><b>0x9FFF<\/b><\/code>, to je bu\u010f bit 0, nebo 1. To nen\u00ed p\u0159\u00edli\u0161 pohodln\u00e9: budeme muset neust\u00e1le p\u0159ep\u00ednat abecedu mezi t\u011bmito dv\u011bma hodnotami (tj. utr\u00e1cet t\u0159i bajty). Ale v\u0161imn\u011bte si, \u017ee v dlouh\u00e9m re\u017eimu m\u016f\u017eeme z k\u00f3du ode\u010d\u00edst po\u010det znak\u016f, kter\u00e9 k\u00f3dujeme pomoc\u00ed kr\u00e1tk\u00e9ho re\u017eimu (po v\u0161ech v\u00fd\u0161e uveden\u00fdch kli\u010dk\u00e1ch je to 10240) \u2014 pak se rozsah znak\u016f posune k <code><b>0x2600<\/b><\/code> \u2013 <code><b>0x77FF<\/b><\/code>, a v tomto p\u0159\u00edpad\u011b budou v cel\u00e9m tomto rozsahu nejvy\u0161\u0161\u00ed 4 bity (z 21) rovny 0. Takto budou sekvence znak\u016f pou\u017e\u00edvat dva bajty na znak (co\u017e je optim\u00e1ln\u00ed pro tak velk\u00fd rozsah), ani\u017e by doch\u00e1zelo k p\u0159ep\u00edn\u00e1n\u00ed abecedy. <\/p>\n<h2>Alternativn\u00ed \u0159e\u0161en\u00ed: SCSU, BOCU-1<\/h2>\n<p>\nZnalci Unicode, ji\u017e po p\u0159e\u010dten\u00ed n\u00e1zvu \u010dl\u00e1nku, pravd\u011bpodobn\u011b, sp\u011bchaj\u00ed p\u0159ipomenout, \u017ee p\u0159\u00edmo mezi standardy Unicode existuje <noindex><a rel=\"nofollow\" href=\"https:\/\/www.unicode.org\/reports\/tr6\/tr6-4.html\">Standard Compression Scheme for Unicode<\/a><\/noindex> (SCSU), kter\u00fd popisuje zp\u016fsob k\u00f3dov\u00e1n\u00ed velmi podobn\u00fd tomu, kter\u00fd je pops\u00e1n v \u010dl\u00e1nku.<\/p>\n<p>Mus\u00edm up\u0159\u00edmn\u011b p\u0159iznat: o jeho existenci jsem se dozv\u011bd\u011bl a\u017e pot\u00e9, co jsem se hluboce pono\u0159il do psan\u00ed sv\u00e9ho \u0159e\u0161en\u00ed. Kdybych o n\u011bm v\u011bd\u011bl od za\u010d\u00e1tku, pravd\u011bpodobn\u011b bych se pokusil napsat jeho implementaci m\u00edsto vym\u00fd\u0161len\u00ed vlastn\u00edho p\u0159\u00edstupu.<\/p>\n<p>Co je zaj\u00edmav\u00e9, SCSU pou\u017e\u00edv\u00e1 my\u0161lenky, kter\u00e9 jsou velmi podobn\u00e9 t\u011bm, kter\u00e9 jsem vyvinul nez\u00e1visle (nam\u00edsto pojmu \u201eabecedy\u201c se pou\u017e\u00edvaj\u00ed \u201eokna\u201c, a t\u011bch je k dispozici v\u00edce ne\u017e u m\u011b). Sou\u010dasn\u011b m\u00e1 tento form\u00e1t tak\u00e9 nev\u00fdhody: je trochu bl\u00ed\u017ee k algoritm\u016fm komprese ne\u017e k\u00f3dov\u00e1n\u00ed. Konkr\u00e9tn\u011b standard poskytuje \u0159adu zp\u016fsob\u016f reprezentace, ale ne\u0159\u00edk\u00e1, jak vybrat z nich optim\u00e1ln\u00ed \u2014 encoder mus\u00ed aplikovat n\u011bjak\u00e9 heuristiky. Tak\u017ee SCSU-encoder, kter\u00fd poskytuje dobrou kompresi, bude slo\u017eit\u011bj\u0161\u00ed a objemn\u011bj\u0161\u00ed ne\u017e m\u016fj algoritmus.<\/p>\n<p>Pro srovn\u00e1n\u00ed jsem p\u0159enesl relativn\u011b jednoduchou implementaci SCSU na JavaScript \u2014 objem k\u00f3du se uk\u00e1zal jako srovnateln\u00fd s m\u00fdm UTF-C, ale v n\u011bkter\u00fdch p\u0159\u00edpadech vyk\u00e1zal v\u00fdsledky o des\u00edtky procent hor\u0161\u00ed (n\u011bkdy m\u016f\u017ee dokonce p\u0159ekonat, ale ne o moc). Nap\u0159\u00edklad texty v hebrej\u0161tin\u011b a \u0159e\u010dtin\u011b UTF-C zak\u00f3doval a\u017e <strong>o 60 % l\u00e9pe ne\u017e SCSU<\/strong> (pravd\u011bpodobn\u011b d\u00edky jejich kompaktn\u00edm abeced\u00e1m).<\/p>\n<p>D\u00e1le dod\u00e1v\u00e1m, \u017ee krom\u011b SCSU existuje tak\u00e9 jin\u00fd zp\u016fsob kompaktn\u00edho zobrazen\u00ed Unicode \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Binary_Ordered_Compression_for_Unicode\">BOCU-1<\/a><\/noindex>, ale jeho c\u00edlem je zajistit kompatibilitu s MIME (co\u017e jsem nepot\u0159eboval) a pou\u017e\u00edv\u00e1 pon\u011bkud jin\u00fd p\u0159\u00edstup k k\u00f3dov\u00e1n\u00ed. Jeho efektivitu jsem nehodnotil, ale m\u00e1m pocit, \u017ee nebude vy\u0161\u0161\u00ed ne\u017e SCSU.<\/p>\n<h2>Mo\u017en\u00e9 vylep\u0161en\u00ed<\/h2>\n<p>\nJi\u017e zm\u00edn\u011bn\u00fd algoritmus nen\u00ed univerz\u00e1ln\u00ed kv\u016fli designu (zde se m\u00e9 c\u00edle pravd\u011bpodobn\u011b nejv\u00edce rozch\u00e1zej\u00ed s c\u00edli konsorcia Unicode). Ji\u017e jsem zm\u00ednil, \u017ee byl vyv\u00edjen p\u0159ev\u00e1\u017en\u011b pro jeden \u00fakol (ukl\u00e1d\u00e1n\u00ed v\u00edc jazykov\u00e9ho slovn\u00edku v prefixov\u00e9m stromu) a n\u011bkter\u00e9 jeho rysy se nemus\u00ed hodit pro jin\u00e9 \u00fakoly. Ale to, \u017ee nen\u00ed standardem, m\u016f\u017ee b\u00fdt i v\u00fdhodou \u2014 <strong>m\u016f\u017eete jej snadno p\u0159izp\u016fsobit sv\u00fdm pot\u0159eb\u00e1m<\/strong>.<\/p>\n<p>Nap\u0159\u00edklad lze o\u010dividn\u011b odstranit p\u0159\u00edtomnost stavu, ud\u011blat k\u00f3dov\u00e1n\u00ed stateless \u2014 prost\u011b neaktualizovat prom\u011bnn\u00e9 <code><b>odpisy<\/b><\/code>, <code><b>v\u00fdpadek<\/b><\/code> a <code><b>is21Bit<\/b><\/code> v enkod\u00e9ru a dekod\u00e9ru. V takov\u00e9m p\u0159\u00edpad\u011b nebude mo\u017en\u00e9 efektivn\u011b komprimovat sekvence znak\u016f jednoho abecedy, ale zaru\u010d\u00ed se, \u017ee stejn\u00fd znak bude v\u017edy k\u00f3dov\u00e1n stejn\u00fdmi bajty, bez ohledu na kontext.<\/p>\n<p>Krom\u011b toho lze k\u00f3dova\u010d zam\u011b\u0159it na konkr\u00e9tn\u00ed jazyk, zm\u011bnit v\u00fdchoz\u00ed stav \u2014 nap\u0159\u00edklad p\u0159i zam\u011b\u0159en\u00ed na rusk\u00e9 texty nastavit na za\u010d\u00e1tku enkod\u00e9ru a dekod\u00e9ru <code><b>offs = 0x0400<\/b><\/code> a <code><b>auxOffs = 0<\/b><\/code>. To d\u00e1v\u00e1 smysl zejm\u00e9na v re\u017eimu stateless. Celkov\u011b to bude podobn\u00e9 pou\u017eit\u00ed star\u00e9ho osmitbitov\u00e9ho k\u00f3dov\u00e1n\u00ed, jen to nezbavuje mo\u017enosti vkl\u00e1dat znaky z cel\u00e9ho Unicode podle pot\u0159eby.<\/p>\n<p>Dal\u0161\u00ed nev\u00fdhoda, kterou jsem ji\u017e zm\u00ednil \u2014 v objemov\u00e9m textu k\u00f3dovan\u00e9m v UTF-C neexistuje rychl\u00fd zp\u016fsob, jak naj\u00edt hranici znaku bl\u00edzk\u00e9ho k libovoln\u00e9mu bajtu. Pokud od k\u00f3dovan\u00e9ho bufferu od\u0159\u00edznete posledn\u00edch, \u0159ekn\u011bme, 100 bajt\u016f, riskujete z\u00edsk\u00e1n\u00ed odpadu, se kter\u00fdm nic neud\u011bl\u00e1te. K\u00f3dov\u00e1n\u00ed nen\u00ed ur\u010deno pro ukl\u00e1d\u00e1n\u00ed v\u00edc ne\u017e n\u011bkolika gigabajtov\u00fdch log\u016f, ale to lze obecn\u011b opravit. Bajt <code><b>0xBF<\/b><\/code> by nikdy nem\u011bl b\u00fdt prvn\u00edm bajtem (ale m\u016f\u017ee b\u00fdt druh\u00fdm nebo t\u0159et\u00edm). Proto lze p\u0159i k\u00f3dov\u00e1n\u00ed vkl\u00e1dat sekvenci <code><b>0xBF 0xBF 0xBF<\/b><\/code> ka\u017ed\u00fdch, dejme tomu, 10 Kb \u2014 pak p\u0159i pot\u0159eb\u011b nalezen\u00ed hranice sta\u010d\u00ed proscanovat vybran\u00fd \u00fasek, dokud nenajdete podobn\u00fd marker. Po posledn\u00edm <code><b>0xBF<\/b><\/code> zaru\u010den\u011b bude za\u010d\u00e1tek znaku. (P\u0159i dek\u00f3dov\u00e1n\u00ed tuto sekvenci t\u0159\u00ed byt\u016f, samoz\u0159ejm\u011b, je t\u0159eba ignorovat.)<\/p>\n<h2>Shrnut\u00ed<\/h2>\n<p>\nPokud jste do\u010detli a\u017e sem \u2014 blahop\u0159eji! Douf\u00e1m, \u017ee jste, stejn\u011b jako j\u00e1, z\u00edskali nov\u00e9 znalosti (nebo si osv\u011b\u017eili vzpom\u00ednky) o fungov\u00e1n\u00ed Unicode.<\/p>\n<p><img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/f14b2d815b06a7fda7b77c0a32e7eb75.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<i>Demonstra\u010dn\u00ed str\u00e1nka. Na p\u0159\u00edkladu hebrej\u0161tiny jsou vid\u011bt v\u00fdhody jak p\u0159ed UTF-8, tak p\u0159ed SCSU.<\/i><\/p>\n<p>Nen\u00ed nutn\u00e9 pova\u017eovat v\u00fd\u0161e uveden\u00e9 \u00favahy za z\u00e1sah do standard\u016f. Nicm\u00e9n\u011b jsem celkov\u011b spokojen s v\u00fdsledky sv\u00e9 pr\u00e1ce, a proto se s nimi r\u00e1d <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/deNULL\/utf-c\">pod\u011bl\u00edm<\/a><\/noindex>: nap\u0159\u00edklad, JS knihovna v minifikovan\u00e9 podob\u011b v\u00e1\u017e\u00ed pouh\u00fdch 1710 byt\u016f (a nem\u00e1 \u017e\u00e1dn\u00e9 z\u00e1vislosti, samoz\u0159ejm\u011b). Jak jsem zm\u00ednil v\u00fd\u0161e, s jej\u00edm fungov\u00e1n\u00edm se m\u016f\u017eete sezn\u00e1mit na <noindex><a rel=\"nofollow\" href=\"https:\/\/denull.github.io\/utf-c\/\">demo str\u00e1nce<\/a><\/noindex> (tam je tak\u00e9 soubor text\u016f, na kter\u00fdch ji m\u016f\u017eete porovnat s UTF-8 a SCSU).<\/p>\n<p>Nakonec je\u0161t\u011b jednou upozorn\u00edm na p\u0159\u00edpady, ve kter\u00fdch pou\u017e\u00edvat UTF-C <b>No.<\/b>:<\/p>\n<ul>\n<li>Pokud jsou va\u0161e \u0159et\u011bzce dostate\u010dn\u011b dlouh\u00e9 (od 100-200 znak\u016f). V takov\u00e9m p\u0159\u00edpad\u011b byste m\u011bli uva\u017eovat o vyu\u017eit\u00ed kompresn\u00edch algoritm\u016f jako je deflate.<\/li>\n<li>Pokud pot\u0159ebujete <em>ASCII transparentnost<\/em>, tedy v\u00e1m z\u00e1le\u017e\u00ed na tom, aby v zak\u00f3dovan\u00fdch sekvenc\u00edch nevznikaly ASCII k\u00f3dy, kter\u00e9 nebyly v p\u016fvodn\u00edm \u0159et\u011bzci. Tento po\u017eadavek se d\u00e1 p\u0159edej\u00edt, pokud p\u0159i interakci s extern\u00edmi API (nap\u0159\u00edklad p\u0159i pr\u00e1ci s datab\u00e1z\u00ed) budete p\u0159ed\u00e1vat v\u00fdsledek k\u00f3dov\u00e1n\u00ed jako abstraktn\u00ed sadu byt\u016f, a ne jako \u0159et\u011bzce. Jinak riskujete ne\u010dekan\u00e9 zranitelnosti.<\/li>\n<li>Pokud chcete m\u00edt mo\u017enost rychle naj\u00edt hranice znak\u016f podle libovoln\u00e9ho posunu (nap\u0159\u00edklad p\u0159i po\u0161kozen\u00ed \u010d\u00e1sti \u0159et\u011bzce). To lze ud\u011blat, ale pouze skenov\u00e1n\u00edm \u0159et\u011bzce od za\u010d\u00e1tku (nebo pou\u017eit\u00edm \u00fapravy popsan\u00e9 v p\u0159edchoz\u00ed \u010d\u00e1sti).<\/li>\n<li>Pokud pot\u0159ebujete rychle prov\u00e1d\u011bt operace na obsahu \u0159et\u011bzc\u016f (t\u0159\u00eddit je, hledat v nich pod\u0159et\u011bzce, konkatenovat). Pro to je pot\u0159eba \u0159et\u011bzce nejprve dek\u00f3dovat, tak\u017ee UTF-C bude v t\u011bchto p\u0159\u00edpadech pomalej\u0161\u00ed ne\u017e UTF-8 (ale rychlej\u0161\u00ed ne\u017e kompresn\u00ed algoritmy). Proto\u017ee stejn\u00fd \u0159et\u011bzec je v\u017edy k\u00f3dov\u00e1n stejn\u00fdm zp\u016fsobem, p\u0159esn\u00e9 srovn\u00e1n\u00ed dek\u00f3dov\u00e1n\u00ed nen\u00ed nutn\u00e9, lze je prov\u00e1d\u011bt po bytech.<\/li>\n<\/ul>\n<p><b>Update:<\/b> u\u017eivateli <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/tyomitch\/\"><b>tyomitch<\/b><\/a><\/noindex> <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/521110\/#comment_22139258\">v koment\u00e1\u0159\u00edch n\u00ed\u017ee<\/a><\/noindex> vyv\u011bsil graf, kter\u00fd zd\u016fraz\u0148uje hranici pou\u017eitelnosti UTF-C. Na n\u011bm lze vid\u011bt, \u017ee UTF-C je efektivn\u011bj\u0161\u00ed ne\u017e algoritmus pro v\u0161eobecn\u00e9 pou\u017eit\u00ed (varianty LZW), dokud je balen\u00fd \u0159et\u011bzec krat\u0161\u00ed <b>~140 znak\u016f<\/b> (pravda, poznamen\u00e1m, \u017ee srovn\u00e1n\u00ed bylo provedeno na jednom textu; pro jin\u00e9 jazyky se v\u00fdsledky mohou li\u0161it).<br \/>\n<img decoding=\"async\" alt=\"Je\u0161t\u011b jeden cyklus: uchov\u00e1v\u00e1me unicode \u0159et\u011bzce 30-60 % kompaktn\u011bj\u0161\u00ed ne\u017e UTF-8\" src=\"\/wp-content\/uploads\/2020\/10\/0bc9f35ad2757d656801c6466dda09a8.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>Zdroj: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/521110\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0438 \u043f\u0435\u0440\u0435\u0434 \u0432\u0430\u043c\u0438 \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u044b\u0431\u043e\u0440\u0430 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0438, \u0442\u043e \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0431\u0443\u0434\u0435\u0442 \u042e\u043d\u0438\u043a\u043e\u0434. \u041a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u043d\u043e \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0442\u0443\u0442 \u0442\u043e\u0436\u0435 \u0435\u0441\u0442\u044c \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u2014 UTF-8. \u041e\u043d \u0445\u043e\u0440\u043e\u0448 \u0442\u0435\u043c, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b \u042e\u043d\u0438\u043a\u043e\u0434\u0430, \u043d\u0435 \u0442\u0440\u0430\u0442\u044f \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0431\u0430\u0439\u0442 \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432. \u041f\u0440\u0430\u0432\u0434\u0430, \u0434\u043b\u044f \u044f\u0437\u044b\u043a\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0445 \u043d\u0435 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":95912,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-95911","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0415\u0441\u043b\u0438 \u0432\u044b.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/eshhyo-odin-velosiped-hranim-yunikodnye-stroki-na-30-60-kompaktnee-chem-utf-8\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"cs_CZ\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0415\u0449\u0451 \u043e\u0434\u0438\u043d \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434: \u0445\u0440\u0430\u043d\u0438\u043c \u044e\u043d\u0438\u043a\u043e\u0434\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043d\u0430 30-60% \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0435\u0435, \u0447\u0435\u043c UTF-8 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0415\u0441\u043b\u0438 \u0432\u044b.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/eshhyo-odin-velosiped-hranim-yunikodnye-stroki-na-30-60-kompaktnee-chem-utf-8\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-10-04T23:42:09+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-10-04T23:42:09+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Dal\u0161\u00ed kolo: ukl\u00e1d\u00e1me Unicode \u0159et\u011bzce o 30-60 % kompaktn\u011bji ne\u017e UTF-8 | ProHoster","description":"Pokud vy.","canonical_url":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/eshhyo-odin-velosiped-hranim-yunikodnye-stroki-na-30-60-kompaktnee-chem-utf-8","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"cs_CZ","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0415\u0449\u0451 \u043e\u0434\u0438\u043d \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434: \u0445\u0440\u0430\u043d\u0438\u043c \u044e\u043d\u0438\u043a\u043e\u0434\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043d\u0430 30-60% \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0435\u0435, \u0447\u0435\u043c UTF-8 | ProHoster","og:description":"\u0415\u0441\u043b\u0438 \u0432\u044b.","og:url":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/eshhyo-odin-velosiped-hranim-yunikodnye-stroki-na-30-60-kompaktnee-chem-utf-8","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-10-04T23:42:09+00:00","article:modified_time":"2020-10-04T23:42:09+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"95911","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 10:55:40","updated":"2022-09-29 03:35:04","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts\/95911","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/comments?post=95911"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts\/95911\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media\/95912"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media?parent=95911"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/categories?post=95911"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/tags?post=95911"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}