Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8

Ikiwa wewe ni msanidi programu na unakabiliwa na kazi ya kuchagua encoding, basi Unicode itakuwa karibu daima kuwa suluhisho sahihi. Njia maalum ya uwakilishi inategemea muktadha, lakini mara nyingi kuna jibu la ulimwengu wote hapa pia - UTF-8. Jambo zuri juu yake ni kwamba hukuruhusu kutumia herufi zote za Unicode bila matumizi pia baiti nyingi katika hali nyingi. Kweli, kwa lugha zinazotumia zaidi ya alfabeti ya Kilatini, "sio nyingi" ni angalau baiti mbili kwa kila mhusika. Je, tunaweza kufanya vyema zaidi bila kurudi kwenye usimbaji wa tangulizi ambao unatuwekea kikomo cha vibambo 256 pekee vinavyopatikana?

Hapo chini ninapendekeza kujijulisha na jaribio langu la kujibu swali hili na kutekeleza algorithm rahisi ambayo hukuruhusu kuhifadhi mistari katika lugha nyingi za ulimwengu bila kuongeza upungufu ambao uko kwenye UTF-8.

Kanusho. Mara moja nitafanya uhifadhi kadhaa muhimu: suluhu iliyoelezwa haitolewi kama mbadala wa UTF-8, inafaa tu katika orodha nyembamba ya kesi (zaidi juu yao chini), na hakuna kesi inapaswa kutumika kuingiliana na API za tatu (ambao hata hawajui kuhusu hilo). Mara nyingi, algorithms za ukandamizaji wa kusudi la jumla (kwa mfano, deflate) zinafaa kwa uhifadhi wa kompakt wa idadi kubwa ya data ya maandishi. Kwa kuongezea, tayari katika mchakato wa kuunda suluhisho langu, nilipata kiwango kilichopo katika Unicode yenyewe, ambayo hutatua shida sawa - ni ngumu zaidi (na mara nyingi mbaya zaidi), lakini bado ni kiwango kinachokubalika, na sio tu kuweka. pamoja kwenye goti. Nitakuambia juu yake pia.

Kuhusu Unicode na UTF-8

Kuanza na, maneno machache kuhusu ni nini Unicode ΠΈ UTF-8.

Kama unavyojua, usimbaji wa 8-bit ulikuwa maarufu. Pamoja nao, kila kitu kilikuwa rahisi: herufi 256 zinaweza kuhesabiwa na nambari kutoka 0 hadi 255, na nambari kutoka 0 hadi 255 zinaweza kuwakilishwa kama byte moja. Ikiwa tunarudi mwanzoni, usimbaji wa ASCII ni mdogo kabisa kwa bits 7, kwa hivyo jambo muhimu zaidi katika uwakilishi wake wa byte ni sifuri, na encodings nyingi za 8-bit zinaendana nayo (zinatofautiana tu katika "juu" sehemu, ambapo muhimu zaidi ni moja).

Je, Unicode inatofautiana vipi na usimbaji huo na kwa nini uwakilishi mwingi maalum unahusishwa nayo - UTF-8, UTF-16 (BE na LE), UTF-32? Hebu tupange kwa utaratibu.

Kiwango cha msingi cha Unicode kinaelezea tu mawasiliano kati ya wahusika (na katika baadhi ya matukio, vipengele vya mtu binafsi vya wahusika) na nambari zao. Na kuna idadi nyingi zinazowezekana katika kiwango hiki - kutoka 0x00 kwa 0x10FFFF (vipande 1). Ikiwa tungetaka kuweka nambari katika safu kama hiyo katika kigezo, si baiti 114 wala 112 zingetutosha. Na kwa kuwa wasindikaji wetu hawajaundwa sana kufanya kazi na nambari za baiti tatu, tutalazimika kutumia kadiri baiti 1 kwa kila herufi! Hii ni UTF-2, lakini ni kwa sababu ya "ufujaji" huu kwamba umbizo hili si maarufu.

Kwa bahati nzuri, mpangilio wa herufi ndani ya Unicode sio nasibu. Seti yao yote imegawanywa katika "17"ndege", ambayo kila moja ina 65536 (0x10000""pointi za kanuni" Dhana ya "point point" hapa ni rahisi nambari ya mhusika, iliyokabidhiwa na Unicode. Lakini, kama ilivyotajwa hapo juu, katika Unicode sio herufi za kibinafsi tu zilizohesabiwa, lakini pia sehemu zao na alama za huduma (na wakati mwingine hakuna chochote kinacholingana na nambari - labda kwa sasa, lakini kwetu hii sio muhimu sana), kwa hivyo. ni sahihi zaidi kila wakati huzungumza haswa juu ya idadi ya nambari zenyewe, na sio alama. Hata hivyo, katika zifuatazo, kwa ajili ya ufupi, mara nyingi nitatumia neno "ishara", nikimaanisha neno "point point".

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8
Ndege za Unicode. Kama unaweza kuona, nyingi (ndege 4 hadi 13) bado hazijatumika.

Kinachoshangaza zaidi ni kwamba "massa" yote kuu iko kwenye ndege ya sifuri, inaitwa "Ndege ya Msingi ya Lugha nyingi". Ikiwa mstari una maandishi katika mojawapo ya lugha za kisasa (ikiwa ni pamoja na Kichina), hutaenda zaidi ya ndege hii. Lakini huwezi kukata Unicode iliyosalia pia - kwa mfano, emoji ziko mwishoni mwa Unicode. ndege inayofuata"Ndege ya Nyongeza ya Lugha nyingi"(inaanzia 0x10000 kwa 0x1FFFF) Kwa hivyo UTF-16 hufanya hivi: herufi zote zikianguka ndani Ndege ya Msingi ya Lugha nyingi, zimesimbwa "kama ilivyo" na nambari inayolingana ya baiti mbili. Walakini, nambari zingine katika safu hii hazionyeshi herufi maalum hata kidogo, lakini zinaonyesha kuwa baada ya jozi hii ya ka tunahitaji kuzingatia nyingine - kwa kuchanganya maadili ya ka hizi nne pamoja, tunapata nambari inayofunika. safu nzima halali ya Unicode. Wazo hili linaitwa "wanandoa mbadala" - labda umesikia kuwahusu.

Kwa hivyo UTF-16 inahitaji mbili au (katika hali nadra sana) ka nne kwa "pointi ya nambari". Hii ni bora kuliko kutumia baiti nne kila wakati, lakini Kilatini (na herufi zingine za ASCII) inaposimbwa kwa njia hii inapoteza nusu ya nafasi kwenye sufuri. UTF-8 imeundwa kusahihisha hii: ASCII ndani yake inachukua, kama hapo awali, byte moja tu; kanuni kutoka 0x80 kwa 0x7FF - ka mbili; kutoka 0x800 kwa 0xFFFF - tatu, na kutoka 0x10000 kwa 0x10FFFF - nne. Kwa upande mmoja, alfabeti ya Kilatini imekuwa nzuri: utangamano na ASCII umerudi, na usambazaji ni sawasawa "kuenea" kutoka kwa 1 hadi 4 byte. Lakini alfabeti zaidi ya Kilatini, ole, hazifaidiki kwa njia yoyote ikilinganishwa na UTF-16, na nyingi sasa zinahitaji baiti tatu badala ya mbili - safu iliyofunikwa na rekodi ya baiti mbili imepungua kwa mara 32, na 0xFFFF kwa 0x7FF, na wala Kichina wala, kwa mfano, Kijojiajia hujumuishwa ndani yake. Cyrillic na alfabeti nyingine tano - hurray - bahati, 2 byte kwa kila tabia.

Kwa nini hili linatokea? Wacha tuone jinsi UTF-8 inawakilisha misimbo ya wahusika:
Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8
Moja kwa moja ili kuwakilisha nambari, biti zilizo na alama hutumiwa hapa x. Inaweza kuonekana kuwa katika rekodi mbili-byte kuna bits 11 tu (kati ya 16). Biti zinazoongoza hapa zina kazi ya msaidizi tu. Kwa upande wa rekodi ya baiti nne, biti 21 kati ya 32 zimetengwa kwa nambari ya nambari - inaweza kuonekana kuwa ka tatu (ambazo hutoa jumla ya bits 24) zitatosha, lakini alama za huduma zinakula sana.

Je, hii ni mbaya? Si kweli. Kwa upande mmoja, ikiwa tunajali sana kuhusu nafasi, tuna kanuni za ukandamizaji ambazo zinaweza kuondoa kwa urahisi entropy yote ya ziada na redundancy. Kwa upande mwingine, lengo la Unicode lilikuwa kutoa uwekaji rekodi wa ulimwengu wote iwezekanavyo. Kwa mfano, tunaweza kukabidhi mstari uliosimbwa katika UTF-8 kwa msimbo ambao hapo awali ulifanya kazi na ASCII, na usiogope kwamba utaona herufi kutoka safu ya ASCII ambayo haipo (baada ya yote, katika UTF-8 yote. ka kuanzia na sifuri kidogo - hivi ndivyo ASCII ilivyo). Na ikiwa ghafla tunataka kukata mkia mdogo kutoka kwa kamba kubwa bila kuifunga tangu mwanzo (au kurejesha sehemu ya habari baada ya sehemu iliyoharibiwa), ni rahisi kwetu kupata kukabiliana ambapo tabia huanza (inatosha). kuruka baiti zilizo na kiambishi awali kidogo 10).

Kwa nini basi kuvumbua kitu kipya?

Wakati huo huo, kuna hali wakati algorithms za kukandamiza kama vile deflate hazitumiki vizuri, lakini unataka kufikia uhifadhi wa kamba. Binafsi, nilikutana na shida hii wakati wa kufikiria juu ya ujenzi mti wa kiambishi ulioshinikizwa kwa kamusi kubwa ikijumuisha maneno katika lugha holela. Kwa upande mmoja, kila neno ni fupi sana, kwa hivyo kukandamiza hakutakuwa na ufanisi. Kwa upande mwingine, utekelezaji wa mti ambao nilizingatia uliundwa ili kila byte ya kamba iliyohifadhiwa itoe vertex ya mti tofauti, kwa hivyo kupunguza idadi yao ilikuwa muhimu sana. Katika maktaba yangu Az.js (Kama katika pymorphy2, ambayo inategemea) shida kama hiyo inaweza kutatuliwa kwa urahisi - kamba zilizowekwa ndani DAWG-kamusi, iliyohifadhiwa humo ndani CP1251 nzuri ya zamani. Lakini, kama ilivyo rahisi kuelewa, hii inafanya kazi vizuri tu kwa alfabeti ndogo - mstari katika Kichina hauwezi kuongezwa kwa kamusi kama hiyo.

Kando, ningependa kutambua nuance moja zaidi isiyofurahisha ambayo hutokea wakati wa kutumia UTF-8 katika muundo wa data kama hiyo. Picha hapo juu inaonyesha kwamba wakati mhusika ameandikwa kama ka mbili, biti zinazohusiana na nambari yake haziji kwa safu, lakini hutenganishwa na jozi ya biti. 10 katikati: 110xxxxx 10xxxxxx. Kwa sababu hii, wakati bits 6 za chini za byte ya pili zinafurika katika msimbo wa tabia (yaani, mpito hutokea. 10111111 β†’ 10000000), basi byte ya kwanza inabadilika pia. Inabadilika kuwa barua "p" inaonyeshwa na byte 0xD0 0xBF, na "r" inayofuata tayari iko 0xD1 0x80. Katika mti wa kiambishi awali, hii inasababisha mgawanyiko wa nodi ya mzazi kuwa mbili - moja kwa kiambishi awali 0xD0, na mwingine kwa 0xD1 (ingawa alfabeti nzima ya Kisirili inaweza kusimba kwa baiti ya pili pekee).

Nilipata nini

Ninakabiliwa na tatizo hili, niliamua kufanya mazoezi ya kucheza michezo na bits, na wakati huo huo kupata ujuzi kidogo na muundo wa Unicode kwa ujumla. Matokeo yake yalikuwa umbizo la usimbaji la UTF-C ("C" kwa Compact), ambayo haitumii zaidi ya ka 3 kwa kila nukta ya nambari, na mara nyingi hukuruhusu kutumia tu baiti moja ya ziada kwa laini nzima iliyosimbwa. Hii inaongoza kwa ukweli kwamba kwenye alfabeti nyingi zisizo za ASCII encoding vile hugeuka kuwa 30-60% iliyoshikamana zaidi kuliko UTF-8.

Nimewasilisha mifano ya utekelezaji wa encoding na decoding algorithms katika fomu JavaScript na maktaba ya Go, unaweza kuzitumia bila malipo katika msimbo wako. Lakini bado nitasisitiza kwamba kwa namna fulani muundo huu unabaki "baiskeli", na siipendekeza kuitumia bila kujua kwanini unahitaji. Hili bado ni jaribio zaidi kuliko "uboreshaji mkubwa wa UTF-8". Walakini, nambari iliyo hapo imeandikwa kwa uzuri, kwa ufupi, na idadi kubwa ya maoni na chanjo ya majaribio.

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8
Matokeo ya mtihani na kulinganisha na UTF-8

Mimi pia nilifanya ukurasa wa onyesho, ambapo unaweza kutathmini utendaji wa algorithm, na kisha nitakuambia zaidi kuhusu kanuni zake na mchakato wa maendeleo.

Kuondoa bits zisizohitajika

Nilichukua UTF-8 kama msingi, bila shaka. Jambo la kwanza na la wazi zaidi ambalo linaweza kubadilishwa ndani yake ni kupunguza idadi ya bits za huduma katika kila byte. Kwa mfano, baiti ya kwanza katika UTF-8 huanza na aidha 0, au na 11 - kiambishi awali 10 Ni baiti zifuatazo pekee ndizo zilizo nayo. Hebu tubadilishe kiambishi awali 11 juu ya 1, na kwa baiti zinazofuata tutaondoa viambishi awali kabisa. Nini kitatokea?

0xxxxxxx - 1 baiti
10xxxxxx xxxxxxxx - 2 ka
110xxxxx xxxxxxxx xxxxxxxx - 3 ka

Subiri, iko wapi rekodi ya baiti nne? Lakini haihitajiki tena - tunapoandika kwa baiti tatu, sasa tuna biti 21 zinazopatikana na hii inatosha kwa nambari zote hadi. 0x10FFFF.

Tumetoa nini hapa? Jambo muhimu zaidi ni ugunduzi wa mipaka ya wahusika kutoka kwa eneo la kiholela kwenye bafa. Hatuwezi kuelekeza kwenye byte ya kiholela na kupata mwanzo wa herufi inayofuata kutoka kwayo. Hiki ni kikwazo cha umbizo letu, lakini katika mazoezi hii ni mara chache muhimu. Kwa kawaida tunaweza kupitia bafa kutoka mwanzo kabisa (haswa inapokuja kwa mistari mifupi).

Hali na lugha za kufunika na ka 2 pia imekuwa bora: sasa muundo wa baiti mbili unatoa anuwai ya biti 14, na hizi ni nambari hadi 0x3FFF. Wachina hawana bahati (wahusika wao mara nyingi huanzia 0x4E00 kwa 0x9FFF), lakini Wageorgia na watu wengine wengi wanafurahiya zaidi - lugha zao pia zinafaa katika ka 2 kwa kila mhusika.

Ingiza hali ya kusimba

Wacha sasa tufikirie juu ya mali ya mistari yenyewe. Kamusi mara nyingi huwa na maneno yaliyoandikwa kwa herufi za alfabeti sawa, na hii pia ni kweli kwa maandishi mengine mengi. Itakuwa nzuri kuashiria alfabeti hii mara moja, na kisha uonyeshe nambari tu ya herufi ndani yake. Wacha tuone ikiwa mpangilio wa wahusika kwenye jedwali la Unicode utatusaidia.

Kama ilivyoelezwa hapo juu, Unicode imegawanywa katika ndege Nambari 65536 kila moja. Lakini hii sio mgawanyiko muhimu sana (kama ilivyosemwa tayari, mara nyingi tuko kwenye ndege ya sifuri). Kuvutia zaidi ni mgawanyiko na vitalu. Masafa haya hayana tena urefu uliopangwa, na yana maana zaidi - kama sheria, kila moja inachanganya herufi kutoka kwa alfabeti sawa.

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8
Sehemu iliyo na herufi za alfabeti ya Kibengali. Kwa bahati mbaya, kwa sababu za kihistoria, huu ni mfano wa ufungashaji mnene sana - herufi 96 zimetawanyika kwa fujo katika sehemu 128 za msimbo wa kuzuia.

Mwanzo wa vitalu na ukubwa wao daima ni nyingi za 16 - hii inafanywa kwa urahisi. Kwa kuongezea, vizuizi vingi huanza na kuishia kwa maadili ambayo ni zidishi za 128 au hata 256 - kwa mfano, alfabeti ya kimsingi ya Kicyrillic inachukua byte 256 kutoka. 0x0400 kwa 0x04FF. Hii ni rahisi sana: ikiwa tutahifadhi kiambishi awali mara moja 0x04, basi herufi yoyote ya Kicyrillic inaweza kuandikwa kwa baiti moja. Kweli, kwa njia hii tutapoteza fursa ya kurudi kwa ASCII (na kwa wahusika wengine wowote kwa ujumla). Kwa hivyo tunafanya hivi:

  1. Baiti mbili 10yyyyyy yxxxxxxx sio tu kuashiria ishara na nambari yyyyyy yxxxxxxx, lakini pia mabadiliko alfabeti ya sasa juu ya yyyyyy y0000000 (yaani, tunakumbuka sehemu zote isipokuwa zile muhimu sana 7 kidogo);
  2. Baiti moja 0xxxxxxx hii ni tabia ya alfabeti ya sasa. Inahitaji tu kuongezwa kwa kukabiliana na tuliyokumbuka katika hatua ya 1. Ingawa hatukubadilisha alfabeti, kukabiliana ni sifuri, kwa hivyo tulidumisha utangamano na ASCII.

Vivyo hivyo kwa misimbo inayohitaji baiti 3:

  1. Baiti tatu 110yyyyy yxxxxxxx xxxxxxxx onyesha ishara yenye nambari yyyyyy yxxxxxxx xxxxxxxx, mabadiliko alfabeti ya sasa juu ya yyyyyy y0000000 00000000 (alikumbuka kila kitu isipokuwa wale wadogo 15 kidogo), na uteue kisanduku ambacho tuko ndani yake ndefu hali (wakati wa kubadilisha alfabeti kurudi kwa baiti mbili, tutaweka upya bendera hii);
  2. Baiti mbili 0xxxxxxx xxxxxxxx katika hali ya muda mrefu ni tabia ya alfabeti ya sasa. Vile vile, tunaiongeza kwa kukabiliana na hatua ya 1. Tofauti pekee ni kwamba sasa tunasoma byte mbili (kwa sababu tulibadilisha hali hii).

Inasikika vizuri: sasa wakati tunahitaji kusimba herufi kutoka safu sawa ya Unicode ya 7-bit, tunatumia baiti 1 ya ziada mwanzoni na jumla ya baiti moja kwa kila herufi.

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8
Kufanya kazi kutoka kwa moja ya matoleo ya awali. Tayari mara nyingi hupiga UTF-8, lakini bado kuna nafasi ya kuboresha.

Nini mbaya zaidi? Kwanza, tuna hali, yaani alfabeti ya sasa ya kukabiliana na kisanduku cha kuteua mode ndefu. Hii inatuwekea kikomo zaidi: sasa herufi zile zile zinaweza kusimbwa kwa njia tofauti katika miktadha tofauti. Kutafuta substrings, kwa mfano, itabidi kufanywa kwa kuzingatia hili, na si tu kwa kulinganisha byte. Pili, mara tu tulipobadilisha alfabeti, ikawa mbaya na usimbuaji wa herufi za ASCII (na hii sio tu alfabeti ya Kilatini, lakini pia alama za msingi, pamoja na nafasi) - zinahitaji kubadilisha alfabeti tena hadi 0, ambayo ni, tena byte ya ziada (na kisha nyingine ili kurudi kwenye hoja yetu kuu).

Alfabeti moja ni nzuri, mbili ni bora zaidi

Wacha tujaribu kubadilisha viambishi vyetu kidogo, tukipunguza moja zaidi hadi tatu zilizoelezewa hapo juu:

0xxxxxxx β€” 1 baiti katika hali ya kawaida, 2 katika hali ndefu
11xxxxxx - 1 baiti
100xxxxx xxxxxxxx - 2 ka
101xxxxx xxxxxxxx xxxxxxxx - 3 ka

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8

Sasa katika rekodi mbili-byte kuna kidogo inapatikana kidogo - kanuni pointi hadi 0x1FFFna sio 0x3FFF. Walakini, bado ni kubwa zaidi kuliko nambari mbili za UTF-8, lugha za kawaida bado zinafaa, hasara inayoonekana zaidi imeanguka. hiragana ΠΈ katakana, Wajapani wana huzuni.

Kanuni hii mpya ni ipi? 11xxxxxx? Hii ni "stash" ndogo ya herufi 64 kwa saizi, inakamilisha alfabeti yetu kuu, kwa hivyo niliiita msaidizi (msaidizi) alfabeti. Tunapobadilisha alfabeti ya sasa, kipande cha alfabeti ya zamani kinakuwa msaidizi. Kwa mfano, tulibadilisha kutoka ASCII hadi Cyrillic - stash sasa ina herufi 64 zilizo na Alfabeti ya Kilatini, nambari, nafasi na koma (kuingizwa mara kwa mara katika maandishi yasiyo ya ASCII). Rudi kwa ASCII - na sehemu kuu ya alfabeti ya Cyrilli itakuwa alfabeti msaidizi.

Shukrani kwa ufikiaji wa alfabeti mbili, tunaweza kushughulikia idadi kubwa ya maandishi na gharama ndogo za kubadili alfabeti (punctuation mara nyingi itasababisha kurudi kwa ASCII, lakini baada ya hapo tutapata herufi nyingi zisizo za ASCII kutoka kwa alfabeti ya ziada, bila kubadili tena).

Bonasi: kiambishi awali cha alfabeti ndogo 11xxxxxx na kuchagua kukabiliana na awali kuwa 0xC0, tunapata utangamano wa sehemu na CP1252. Kwa maneno mengine, maandishi mengi (lakini si yote) ya Ulaya Magharibi yaliyosimbwa katika CP1252 yataonekana sawa katika UTF-C.

Hapa, hata hivyo, ugumu unatokea: jinsi ya kupata msaidizi kutoka kwa alfabeti kuu? Unaweza kuacha kukabiliana sawa, lakini - ole - hapa muundo wa Unicode tayari unacheza dhidi yetu. Mara nyingi sehemu kuu ya alfabeti sio mwanzoni mwa kizuizi (kwa mfano, mji mkuu wa Urusi "A" una nambari. 0x0410, ingawa kizuizi cha Cyrilli huanza na 0x0400) Kwa hivyo, baada ya kuchukua herufi 64 za kwanza kwenye stash, tunaweza kupoteza ufikiaji wa sehemu ya mkia ya alfabeti.

Ili kurekebisha tatizo hili, nilipitia kwa mikono baadhi ya vizuizi vinavyolingana na lugha tofauti, na nikabainisha urekebishaji wa alfabeti ya usaidizi ndani ya ile kuu kwao. Alfabeti ya Kilatini, isipokuwa, ilipangwa upya kama msingi64.

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8

Miguso ya mwisho

Hebu hatimaye tufikirie ni wapi pengine tunaweza kuboresha kitu.

Kumbuka kwamba umbizo 101xxxxx xxxxxxxx xxxxxxxx hukuruhusu kusimba nambari hadi 0x1FFFFF, na Unicode inaisha mapema, saa 0x10FFFF. Kwa maneno mengine, nambari ya mwisho ya nambari itawakilishwa kama 10110000 11111111 11111111. Kwa hiyo, tunaweza kusema kwamba ikiwa byte ya kwanza ni ya fomu 1011xxxx (Wapi xxxx kubwa kuliko 0), basi inamaanisha kitu kingine. Kwa mfano, unaweza kuongeza herufi zingine 15 huko ambazo zinapatikana kila wakati kwa usimbuaji katika byte moja, lakini niliamua kuifanya kwa njia tofauti.

Wacha tuangalie vizuizi hivyo vya Unicode ambavyo vinahitaji baiti tatu sasa. Kimsingi, kama ilivyotajwa tayari, hawa ni wahusika wa Kichina - lakini ni ngumu kufanya chochote nao, kuna elfu 21 kati yao. Lakini hiragana na katakana pia waliruka huko - na hakuna wengi wao tena, chini ya mia mbili. Na, kwa kuwa tulikumbuka Wajapani, pia kuna emojis (kwa kweli, zimetawanyika katika sehemu nyingi kwenye Unicode, lakini vizuizi kuu viko kwenye safu. 0x1F300 - 0x1FBFF) Ikiwa unafikiria juu ya ukweli kwamba sasa kuna emojis ambazo zimekusanywa kutoka kwa vidokezo kadhaa kwa wakati mmoja (kwa mfano, emojiBaiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8 lina nambari nyingi kama 7!), basi inakuwa aibu kabisa kutumia ka tatu kwa kila (7 Γ— 3 = 21 byte kwa ajili ya ikoni moja, jinamizi).

Kwa hivyo, tunachagua safu chache zilizochaguliwa zinazolingana na emoji, hiragana na katakana, tuzipe nambari tena katika orodha moja endelevu na kuzisimba kama baiti mbili badala ya tatu:

1011xxxx xxxxxxxx

Kubwa: emoji iliyotajwa hapo juuBaiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8, inayojumuisha pointi 7 za msimbo, inachukua baiti 8 katika UTF-25, na tunaiweka ndani 14 (haswa ka mbili kwa kila nukta ya nambari). Kwa njia, Habr alikataa kuchimba (wote wa zamani na katika mhariri mpya), kwa hivyo nililazimika kuiingiza na picha.

Hebu tujaribu kurekebisha tatizo moja zaidi. Kama tunavyokumbuka, alfabeti ya msingi ni kimsingi biti 6 za juu, ambayo tunakumbuka na gundi kwa msimbo wa kila ishara inayofuata iliyopangwa. Kwa upande wa wahusika wa Kichina ambao wako kwenye block 0x4E00 - 0x9FFF, hii ni kidogo 0 au 1. Hii sio rahisi sana: tutahitaji kubadilisha kila mara alfabeti kati ya maadili haya mawili (yaani kutumia baiti tatu). Lakini kumbuka kuwa katika hali ndefu, kutoka kwa nambari yenyewe tunaweza kutoa idadi ya wahusika ambao tunasindika kwa kutumia hali fupi (baada ya hila zote zilizoelezewa hapo juu, hii ni 10240) - basi safu ya hieroglyphs itahamia. 0x2600 - 0x77FF, na katika kesi hii, katika safu hii yote, bits 6 muhimu zaidi (kati ya 21) zitakuwa sawa na 0. Kwa hivyo, mlolongo wa hieroglyphs utatumia byte mbili kwa hieroglyph (ambayo ni bora kwa safu kubwa kama hiyo), bila kusababisha swichi za alfabeti.

Ufumbuzi mbadala: SCSU, BOCU-1

Wataalam wa Unicode, wakiwa wamesoma tu kichwa cha kifungu, wataharakisha kukukumbusha kuwa moja kwa moja kati ya viwango vya Unicode kuna. Mpango wa Mfinyazo wa Kawaida wa Unicode (SCSU), ambayo inaelezea njia ya usimbuaji sawa na ile iliyoelezewa katika kifungu.

Ninakiri kwa uaminifu: Nilijifunza kuhusu kuwepo kwake tu baada ya kuzama sana katika kuandika uamuzi wangu. Laiti ningelijua hilo tangu mwanzo, labda ningejaribu kuandika utekelezaji badala ya kuja na mbinu yangu mwenyewe.

Kinachofurahisha ni kwamba SCSU hutumia mawazo yanayofanana sana na yale niliyokuja nayo peke yangu (badala ya dhana ya "alfabeti" wanatumia "madirisha", na kuna zaidi yao yanapatikana kuliko mimi). Wakati huo huo, muundo huu pia una hasara: ni karibu kidogo na algorithms ya compression kuliko encoding. Hasa, kiwango hutoa njia nyingi za uwakilishi, lakini haisemi jinsi ya kuchagua mojawapo - kwa hili, encoder lazima atumie aina fulani ya heuristics. Kwa hivyo, encoder ya SCSU ambayo hutoa ufungaji mzuri itakuwa ngumu zaidi na ngumu zaidi kuliko algorithm yangu.

Kwa kulinganisha, nilihamisha utekelezaji rahisi wa SCSU kwa JavaScript - kwa suala la kiasi cha nambari iligeuka kulinganishwa na UTF-C yangu, lakini katika hali zingine matokeo yalikuwa makumi ya asilimia mbaya (wakati mwingine inaweza kuzidi, lakini sio sana). Kwa mfano, maandishi katika Kiebrania na Kigiriki yalisimbwa na UTF-C 60% bora kuliko SCSU (labda kutokana na alfabeti zao za kompakt).

Kando, nitaongeza kuwa kando na SCSU pia kuna njia nyingine ya kuwakilisha Unicode - BOCU-1, lakini inalenga utangamano wa MIME (ambayo sikuihitaji) na inachukua mbinu tofauti kidogo ya usimbuaji. Sijatathmini ufanisi wake, lakini inaonekana kwangu kuwa haiwezekani kuwa juu kuliko SCSU.

Maboresho yanayowezekana

Algorithm niliyowasilisha sio ya ulimwengu wote kwa muundo (hapa pengine ndipo malengo yangu yanatofautiana zaidi na malengo ya Unicode Consortium). Tayari nimetaja kwamba ilitengenezwa hasa kwa ajili ya kazi moja (kuhifadhi kamusi ya lugha nyingi katika mti wa kiambishi awali), na baadhi ya vipengele vyake huenda havifai kwa kazi nyinginezo. Lakini ukweli kwamba sio kiwango unaweza kuwa pamoja - unaweza kuirekebisha kwa urahisi ili kuendana na mahitaji yako.

Kwa mfano, kwa njia dhahiri unaweza kuondoa uwepo wa serikali, tengeneza uwekaji misimbo usio na uraia - usisasishe vijiti offs, auxOffs ΠΈ is21Bit katika encoder na avkodare. Katika kesi hii, haitawezekana kufunga mfululizo wa herufi za alfabeti sawa, lakini kutakuwa na hakikisho kwamba tabia hiyo hiyo inasimbwa kila wakati na baiti sawa, bila kujali muktadha.

Kwa kuongeza, unaweza kurekebisha encoder kwa lugha maalum kwa kubadilisha hali ya msingi - kwa mfano, kuzingatia maandiko ya Kirusi, kuweka encoder na decoder mwanzoni. offs = 0x0400 ΠΈ auxOffs = 0. Hii ina maana hasa katika kesi ya hali isiyo na uraia. Kwa ujumla, hii itakuwa sawa na kutumia encoding ya zamani ya-bit nane, lakini bila kuondoa uwezo wa kuingiza wahusika kutoka kwa Unicode zote kama inahitajika.

Kikwazo kingine kilichotajwa hapo awali ni kwamba katika maandishi makubwa yaliyosimbwa katika UTF-C hakuna njia ya haraka ya kupata mpaka wa herufi karibu na byte ya kiholela. Ukikata ya mwisho, sema, ka 100 kutoka kwa bafa iliyosimbwa, una hatari ya kupata takataka ambayo huwezi kufanya nayo chochote. Encoding haijaundwa kwa ajili ya kuhifadhi kumbukumbu za gigabyte nyingi, lakini kwa ujumla hii inaweza kusahihishwa. Byte 0xBF haipaswi kamwe kuonekana kama baiti ya kwanza (lakini inaweza kuwa ya pili au ya tatu). Kwa hiyo, wakati wa encoding, unaweza kuingiza mlolongo 0xBF 0xBF 0xBF kila, sema, 10 KB - basi, ikiwa unahitaji kupata mpaka, itakuwa ya kutosha kuchunguza kipande kilichochaguliwa mpaka alama sawa inapatikana. Kufuatia ya mwisho 0xBF imehakikishwa kuwa mwanzo wa mhusika. (Wakati wa kusimbua, mlolongo huu wa ka tatu, bila shaka, utahitaji kupuuzwa.)

Akihitimisha

Ikiwa umesoma hadi hapa, pongezi! Natumai wewe, kama mimi, umejifunza kitu kipya (au kuburudisha kumbukumbu yako) kuhusu muundo wa Unicode.

Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8
Ukurasa wa onyesho. Mfano wa Kiebrania unaonyesha faida zaidi ya UTF-8 na SCSU.

Utafiti ulioelezwa hapo juu haupaswi kuchukuliwa kuwa ni ukiukaji wa viwango. Walakini, kwa ujumla nimeridhika na matokeo ya kazi yangu, kwa hivyo ninafurahiya nao kushiriki: kwa mfano, maktaba ya JS iliyopunguzwa ina uzito wa ka 1710 tu (na haina tegemezi, bila shaka). Kama nilivyosema hapo juu, kazi yake inaweza kupatikana ukurasa wa onyesho (pia kuna seti ya maandishi ambayo inaweza kulinganishwa na UTF-8 na SCSU).

Mwishowe, kwa mara nyingine tena nitazingatia kesi ambazo UTF-C inatumika sio thamani:

  • Ikiwa mistari yako ni ndefu ya kutosha (kutoka kwa herufi 100-200). Katika kesi hii, unapaswa kufikiria juu ya kutumia algorithms ya compression kama deflate.
  • Ikiwa unahitaji Uwazi wa ASCII, yaani, ni muhimu kwako kwamba mfuatano uliosimbwa hauna misimbo ya ASCII ambayo haikuwa kwenye mfuatano wa asili. Haja ya hii inaweza kuepukwa ikiwa, wakati wa kuingiliana na API za watu wengine (kwa mfano, kufanya kazi na hifadhidata), utapitisha matokeo ya usimbaji kama seti dhahania ya baiti, na sio kama kamba. Vinginevyo, una hatari ya kupata udhaifu usiotarajiwa.
  • Ikiwa unataka kuwa na uwezo wa kupata haraka mipaka ya wahusika kwa kukabiliana kiholela (kwa mfano, wakati sehemu ya mstari imeharibiwa). Hii inaweza kufanyika, lakini tu kwa skanning mstari tangu mwanzo (au kutumia marekebisho yaliyoelezwa katika sehemu ya awali).
  • Ikiwa unahitaji haraka kufanya shughuli kwenye yaliyomo kwenye masharti (yapange, tafuta substrings ndani yao, concatenate). Hii inahitaji mifuatano iamuliwe kwanza, kwa hivyo UTF-C itakuwa polepole kuliko UTF-8 katika hali hizi (lakini haraka kuliko algoriti za mbano). Kwa kuwa mfuatano huo huo husimbwa kila mara kwa njia ile ile, ulinganisho kamili wa utatuzi hauhitajiki na unaweza kufanywa kwa misingi ya byte-by-byte.

Update: user Tyomitch katika maoni hapa chini ilichapisha grafu inayoangazia vikomo vya utumiaji vya UTF-C. Inaonyesha kuwa UTF-C ni bora zaidi kuliko algorithm ya kusudi la jumla (tofauti ya LZW) mradi tu kamba iliyopakiwa ni fupi. ~ herufi 140 (hata hivyo, ninaona kuwa ulinganisho ulifanywa kwa maandishi moja; kwa lugha zingine matokeo yanaweza kutofautiana).
Baiskeli nyingine: tunahifadhi masharti ya Unicode 30-60% zaidi kuliko UTF-8

Chanzo: mapenzi.com

Kuongeza maoni