Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8

Yen sampeyan dadi pangembang lan ngadhepi tugas milih enkoding, Unicode bakal dadi solusi sing tepat. Cara perwakilan tartamtu gumantung ing konteks, nanging paling asring ana jawaban universal ing kene - UTF-8. Sing apik babagan iki yaiku ngidini sampeyan nggunakake kabeh karakter Unicode tanpa mbuwang uga akeh bita ing akeh kasus. Bener, kanggo basa sing nggunakake luwih saka mung aksara Latin, "ora kakehan" paling ora rong bita saben karakter. Apa kita bisa nindakake luwih apik tanpa bali menyang enkoding prasejarah sing mbatesi kita mung 256 karakter sing kasedhiya?

Ing ngisor iki aku ngusulake supaya kenal karo upaya kanggo mangsuli pitakon iki lan ngetrapake algoritma sing cukup prasaja sing ngidini sampeyan nyimpen garis ing pirang-pirang basa ing saindenging jagad tanpa nambah redundansi ing UTF-8.

Penafian. Aku bakal langsung nggawe sawetara leladen penting: solusi diterangake ora ana minangka panggantos universal kanggo UTF-8, iku mung cocok ing dhaftar panah cilik (liyane ing ngisor iki), lan ing kasus ora kudu digunakake kanggo sesambungan karo API pihak katelu (sing malah ora ngerti bab iku). Paling asring, algoritma kompresi tujuan umum (contone, deflate) cocok kanggo panyimpenan kompak saka volume data teks sing akeh. Kajaba iku, ing proses nggawe solusi, aku nemokake standar sing wis ana ing Unicode dhewe, sing ngrampungake masalah sing padha - luwih rumit (lan asring luwih elek), nanging isih dadi standar sing ditampa, lan ora mung dilebokake. bebarengan ing dhengkul. Aku uga bakal ngandhani sampeyan babagan dheweke.

Babagan Unicode lan UTF-8

Kanggo miwiti, sawetara tembung babagan apa iku Unicode и UTF-8.

Kaya sing sampeyan ngerteni, enkoding 8-bit biyen populer. Karo wong-wong mau, kabeh iku prasaja: 256 karakter bisa diwenehi nomer saka 0 kanggo 255, lan nomer saka 0 kanggo 255 temenan bisa dituduhake minangka siji bait. Yen kita bali menyang wiwitan, enkoding ASCII rampung diwatesi dadi 7 bit, saéngga bit sing paling penting ing perwakilan byte yaiku nol, lan umume enkoding 8-bit kompatibel karo (mung beda-beda ing "ndhuwur"). bagean, ing ngendi bit sing paling penting yaiku siji).

Kepiye Unicode beda karo enkoding kasebut lan kenapa akeh perwakilan khusus sing ana gandhengane - UTF-8, UTF-16 (BE lan LE), UTF-32? Ayo diurutake kanthi urut.

Standar Unicode dhasar mung nggambarake korespondensi antarane karakter (lan ing sawetara kasus, komponen individu saka karakter) lan nomer. Lan ana akeh nomer bisa ing standar iki - saka 0x00 kanggo 0x10FFFF (1 bêsik). Yen kita pengin nglebokake nomer ing kisaran kasebut dadi variabel, 114 utawa 112 bait ora cukup kanggo kita. Lan amarga prosesor kita ora dirancang banget kanggo nggarap nomer telung bait, kita bakal dipeksa nggunakake 1 bait saben karakter! Iki UTF-2, nanging sabenere amarga iki "wastefulness" format iki ora populer.

Untunge, urutan karakter ing Unicode ora acak. Kabeh set dipérang dadi 17 "pesawat", sing saben ngemot 65536 (0x10000) «titik kode" Konsep "titik kode" ing kene mung prasaja nomer karakter, ditugasake dening Unicode. Nanging, kaya sing kasebut ing ndhuwur, ing Unicode ora mung karakter individu sing diwenehi nomer, nanging uga komponen lan tandha layanan (lan kadhangkala ora ana sing cocog karo nomer kasebut - mbok menawa saiki, nanging kanggo kita iki ora penting banget), dadi iku luwih bener tansah pirembagan khusus bab nomer nomer piyambak, lan ora simbol. Nanging, ing ngisor iki, kanggo ringkesan, aku bakal kerep nggunakake tembung "simbol", sing nuduhake istilah "titik kode".

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8
pesawat Unicode. Kaya sing sampeyan ngerteni, umume (pesawat 4 nganti 13) isih ora digunakake.

Sing paling nggumunake yaiku kabeh "pulp" utama dumunung ing bidang nol, diarani "Bidang Multilingual Dasar". Yen baris ngemot teks ing salah sawijining basa modern (kalebu Cina), sampeyan ora bakal ngluwihi pesawat iki. Nanging sampeyan uga ora bisa ngilangi Unicode liyane - contone, emoji biasane ana ing mburi pesawat sabanjure,"Bidang Multilingual Tambahan"(Iku ngluwihi saka 0x10000 kanggo 0x1FFFF). Dadi UTF-16 nindakake iki: kabeh karakter sing ana ing njero Bidang Multilingual Dasar, dienkode "kaya" karo nomer loro-bait sing cocog. Nanging, sawetara angka ing kisaran iki ora nuduhake karakter tartamtu, nanging nuduhake yen sawise pasangan bait iki, kita kudu nimbang siji liyane - kanthi nggabungake nilai saka papat bait iki bebarengan, kita entuk nomer sing nutupi. kabeh kisaran Unicode sing sah. Ide iki diarani "pasangan pengganti" - sampeyan bisa uga wis krungu.

Dadi UTF-16 mbutuhake loro utawa (ing kasus arang banget) papat bait saben "titik kode". Iki luwih apik tinimbang nggunakake papat bait kabeh wektu, nanging Latin (lan karakter ASCII liyane) nalika dienkode cara iki sampah setengah spasi ing nul. UTF-8 dirancang kanggo mbenerake iki: ASCII ing occupies, minangka sadurunge, mung siji bait; kode saka 0x80 kanggo 0x7FF - rong bita; saka 0x800 kanggo 0xFFFF - telu, lan saka 0x10000 kanggo 0x10FFFF - papat. Ing tangan siji, aksara Latin wis dadi apik: kompatibilitas karo ASCII bali, lan distribusi luwih merata "nyebar" saka 1 kanggo 4 bait. Nanging aksara liyane saka Latin, sayangé, ora entuk manfaat ing sembarang cara dibandhingake UTF-16, lan akeh saiki mbutuhake telung bita tinimbang loro - sawetara sing dijamin dening cathetan loro-byte wis narrowed dening 32 kaping, karo 0xFFFF kanggo 0x7FF, lan ora Cina utawa, contone, Georgian kalebu ing. Cyrillic lan limang aksara liyane - hurray - begja, 2 bita saben karakter.

Yagene iki kedadeyan? Ayo ndeleng carane UTF-8 makili kode karakter:
Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8
Langsung kanggo makili nomer, bit ditandhani karo simbol digunakake kene x. Bisa dideleng yen ing rekaman rong bait mung ana 11 bit kasebut (saka 16). Bit anjog ing kene mung nduweni fungsi tambahan. Ing kasus rekaman papat bait, 21 saka 32 bit diparengake kanggo nomer kode titik - iku bakal koyone sing telung bait (sing menehi total 24 bit) bakal cukup, nanging layanan spidol mangan munggah kakehan.

Apa iki ala? Ora temenan. Ing tangan siji, yen kita peduli banget babagan spasi, kita duwe algoritma kompresi sing bisa ngilangi kabeh entropi lan redundansi ekstra. Ing sisih liya, tujuan Unicode yaiku nyedhiyakake pengkodean sing paling universal. Contone, kita bisa ngandelake baris sing dienkode ing UTF-8 kanggo kode sing sadurunge mung dianggo karo ASCII, lan ora wedi yen bakal weruh karakter saka sawetara ASCII sing bener ora ana (sawise kabeh, ing UTF-8 kabeh. bita wiwit saka nol bit - iki persis apa ASCII). Lan yen kita tiba-tiba pengin ngethok buntut cilik saka senar gedhe tanpa dekoding saka awal (utawa mulihake bagean informasi sawise bagean rusak), iku gampang kanggo nemokake offset ngendi karakter wiwit (iku cukup. kanggo nglewati bita sing duwe awalan dicokot 10).

Apa banjur invent soko anyar?

Ing wektu sing padha, kadhangkala ana kahanan nalika algoritma kompresi kaya deflate ora bisa ditrapake, nanging sampeyan pengin entuk panyimpenan kompak saka senar. Secara pribadi, aku nemoni masalah iki nalika mikir babagan bangunan wit ater-ater sing dikompres kanggo kamus gedhe kalebu tembung ing basa arbitrer. Ing tangan siji, saben tembung cendhak banget, supaya kompres bakal ora efektif. Ing tangan liyane, implementasine wit sing aku dianggep dirancang supaya saben bait saka senar disimpen kui vertex wit kapisah, supaya minimalake nomer sing banget migunani. Ing perpustakaanku Az.js (Ing pymorphy 2, kang adhedhasar) masalah padha bisa ditanggulangi mung - strings dikempalken menyang DAWG-kamus, disimpen ana ing CP1251 lawas apik. Nanging, kaya sing gampang dingerteni, iki mung dianggo kanggo aksara winates - baris ing basa Cina ora bisa ditambahake ing kamus kasebut.

Dhewe, aku pengin nyathet siji nuansa liyane sing ora nyenengake nalika nggunakake UTF-8 ing struktur data kasebut. Gambar ing ndhuwur nuduhake yen karakter ditulis minangka rong bita, bit sing ana hubungane karo nomer kasebut ora ana ing baris, nanging dipisahake dening pasangan bit. 10 ing tengah: 110xxxxx 10xxxxxx. Amarga iki, nalika 6 bit ngisor saka byte kapindho kebanjiran ing kode karakter (yaiku, transisi ana. 1011111110000000), banjur bait pisanan uga diganti. Pranyata huruf "p" dilambangake dening bait 0xD0 0xBF, lan sabanjure "r" wis 0xD1 0x80. Ing wit ater-ater, iki ndadékaké pamisahan simpul induk dadi loro - siji kanggo awalan. 0xD0, lan liyane kanggo 0xD1 (sanajan kabeh aksara Sirilik bisa dienkode mung dening byte kapindho).

Apa aku entuk

Ngadhepi masalah iki, aku mutusake kanggo latihan main game kanthi bit, lan ing wektu sing padha, luwih ngerti struktur Unicode sacara sakabehe. Asil kasebut yaiku format enkoding UTF-C ("C" kanggo kompak), sing mbuwang ora luwih saka 3 bita saben titik kode, lan asring banget ngidini sampeyan nglampahi mung siji bait ekstra kanggo kabeh baris dienkode. Iki ndadékaké kanggo kasunyatan sing ing akeh aksara non-ASCII encoding kuwi dadi metu 30-60% luwih kompak tinimbang UTF-8.

Aku wis presented conto implementasine encoding lan dekoding algoritma ing wangun Pustaka JavaScript lan Go, sampeyan bisa nggunakake kanthi bebas ing kode sampeyan. Nanging aku isih bakal nandheske manawa format iki tetep dadi "sepeda", lan aku ora nyaranake nggunakake tanpa ngerti sebabe sampeyan butuh. Iki isih luwih saka eksperimen tinimbang "perbaikan UTF-8" sing serius. Nanging, kode kasebut ditulis kanthi rapi, ringkes, kanthi akeh komentar lan jangkoan tes.

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8
Asil tes lan mbandhingake karo UTF-8

Aku uga nindakake kaca demo, ngendi sampeyan bisa ngevaluasi kinerja algoritma, banjur aku bakal pitutur marang kowe liyane babagan prinsip lan proses pembangunan.

Ngilangi bit keluwih

Aku njupuk UTF-8 minangka basis, mesthi. Wangsulan: Bab ingkang pisanan lan paling ketok sing bisa diganti ing iku kanggo ngurangi jumlah bit layanan ing saben byte. Contone, byte pisanan ing UTF-8 tansah diwiwiti karo salah siji 0, utawa karo 11 - ater-ater 10 Mung bita ing ngisor iki duwe. Ayo ganti prefiks 11 ing 1, lan kanggo bita sabanjure kita bakal mbusak prefiks rampung. Apa sing bakal kelakon?

0xxxxxxx - 1 bait
10xxxxxx xxxxxxxx - 2 bita
110xxxxx xxxxxxxx xxxxxxxx - 3 bita

Ngenteni, ngendi rekaman papat bait? Nanging ora perlu maneh - nalika nulis ing telung bita, kita saiki duwe 21 bit kasedhiya lan iki cukup kanggo kabeh nomer nganti 0x10FFFF.

Apa sing wis kita korbanake ing kene? Sing paling penting yaiku deteksi wates karakter saka lokasi sing sewenang-wenang ing buffer. Kita ora bisa nuding byte kasepakatan lan nemokake wiwitan karakter sabanjure. Iki minangka watesan saka format kita, nanging ing laku iki arang perlu. Kita biasane bisa mbukak liwat buffer saka awal banget (utamané nalika nerangake garis cendhak).

Kahanan sing nutupi basa kanthi 2 bait uga dadi luwih apik: saiki format rong bait menehi sawetara 14 bit, lan iki minangka kode nganti 0x3FFF. Wong Tionghoa ora beruntung (karakteré biasane saka 0x4E00 kanggo 0x9FFF), nanging wong Georgia lan akeh wong liya luwih seneng - basane uga cocog karo 2 bita saben karakter.

Ketik status encoder

Saiki ayo mikir babagan sifat garis kasebut. Kamus paling kerep ngemot tembung sing ditulis nganggo aksara alfabet sing padha, lan iki uga bener kanggo teks liyane. Iku bakal apik kanggo nunjukaké alfabet iki sapisan, lan banjur mung nuduhake nomer aksara ing. Ayo ndeleng apa susunan karakter ing tabel Unicode bakal mbantu kita.

Kaya kasebut ing ndhuwur, Unicode dipérang dadi pesawat 65536 kode saben. Nanging iki ora divisi banget migunani (minangka wis ngandika, paling asring kita ing pesawat nul). Luwih menarik yaiku divisi dening pamblokiran Kisaran iki ora duwe dawa tetep, lan luwih migunani - minangka aturan, saben karakter nggabungake aksara saka alfabet sing padha.

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8
Blok sing ngemot karakter aksara Bengali. Sayange, amarga alasan sejarah, iki minangka conto kemasan sing ora kandhel - 96 karakter kasebar kanthi acak ing 128 titik kode blok.

Awal pamblokiran lan ukurane tansah kelipatan 16 - iki rampung mung kanggo penak. Kajaba iku, akeh pamblokiran diwiwiti lan diakhiri ing nilai sing kelipatan 128 utawa malah 256 - contone, alfabet Cyrillic dhasar njupuk 256 bait saka 0x0400 kanggo 0x04FF. Iki cukup trep: yen kita nyimpen awalan sapisan 0x04, banjur sembarang karakter Cyrillic bisa ditulis ing siji bait. Bener, kanthi cara iki kita bakal kelangan kesempatan kanggo bali menyang ASCII (lan karakter liyane ing umum). Mulane kita nindakake iki:

  1. Loro bita 10yyyyyy yxxxxxxx ora mung nuduhake simbol karo nomer yyyyyy yxxxxxxx, nanging uga ngganti aksara saiki ing yyyyyy y0000000 (yaiku, kita ngelingi kabeh bit kajaba sing paling penting 7 dicokot);
  2. siji bait 0xxxxxxx iki karakter aksara saiki. Iku mung kudu ditambahake kanggo nutup kerugian sing kita elinga ing langkah 1. Nalika kita ora ngganti aksara, offset nul, supaya kita maintained kompatibilitas karo ASCII.

Uga kanggo kode sing mbutuhake 3 bita:

  1. Telung bita 110yyyyy yxxxxxxx xxxxxxxx nuduhake simbol karo nomer yyyyyy yxxxxxxx xxxxxxxx, owah aksara saiki ing yyyyyy y0000000 00000000 (inget kabeh kajaba sing luwih enom 15 dicokot), banjur centhang kothak sing saiki ana dawa mode (nalika ngganti aksara bali menyang pindho bait, kita bakal ngreset flag iki);
  2. Loro bita 0xxxxxxx xxxxxxxx ing mode dawa iku karakter aksara saiki. Kajaba iku, kita nambah karo offset saka langkah 1. Bentenipun mung sing saiki kita maca rong bait (amarga kita ngalih menyang mode iki).

Swara apik: saiki nalika kita kudu encode karakter saka kisaran Unicode 7-bit padha, kita nglampahi 1 bait ekstra ing wiwitan lan total siji bait saben karakter.

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8
Makarya saka salah siji saka versi sadurungé. Wis kerep ngalahake UTF-8, nanging isih ana papan kanggo dandan.

Apa sing luwih elek? Kaping pisanan, kita duwe syarat, yaiku ngimbangi aksara saiki lan kothak centhang modus dawa. Iki luwih mbatesi kita: saiki karakter sing padha bisa dikode kanthi beda ing konteks sing beda. Nggoleki substrings, contone, kudu ditindakake kanthi nimbang iki, lan ora mung kanthi mbandhingake bait. Kapindho, sanalika kita ngganti alfabet, dadi ala karo enkoding karakter ASCII (lan iki ora mung aksara Latin, nanging uga wacan dhasar, kalebu spasi) - kudu ngganti aksara maneh kanggo 0, yaiku, maneh bait ekstra (lan siji liyane kanggo bali menyang titik utama).

Siji aksara apik, loro luwih apik

Ayo dadi nyoba kanggo ngganti awalan bit kita sethitik, squeezing ing siji liyane kanggo telung diterangake ing ndhuwur:

0xxxxxxx - 1 bait ing mode normal, 2 ing mode dawa
11xxxxxx - 1 bait
100xxxxx xxxxxxxx - 2 bita
101xxxxx xxxxxxxx xxxxxxxx - 3 bita

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8

Saiki ing rekaman rong bait ana siji bit kurang kasedhiya - titik kode nganti 0x1FFF, lan ora 0x3FFF. Nanging, isih katon luwih gedhe tinimbang kode UTF-8 bait kaping pindho, basa sing paling umum isih pas, kerugian sing paling katon wis ilang. hiragana и katakana, wong Jepang susah.

Apa kode anyar iki? 11xxxxxx? Iki minangka "stash" cilik kanthi ukuran 64 karakter, nglengkapi alfabet utama kita, mula aku nyebutake tambahan (bantu) aksara. Nalika kita ngalih aksara saiki, Piece saka aksara lawas dadi tambahan. Contone, kita ngalih saka ASCII kanggo Cyrillic - stash saiki ngemot 64 karakter ngemot Aksara Latin, angka, spasi lan koma (sisipan paling kerep ing teks non-ASCII). Ngalih maneh menyang ASCII - lan bagean utama aksara Sirilik bakal dadi alfabet tambahan.

Thanks kanggo akses menyang loro aksara, kita bisa nangani nomer akeh teks karo biaya minimal kanggo ngoper aksara (waca bakal paling asring mimpin kanggo bali menyang ASCII, nanging sawise iku kita bakal entuk akeh karakter non-ASCII saka aksara tambahan, tanpa ngalih maneh).

Bonus: prefixing sub-abjad 11xxxxxx lan milih offset dhisikan kanggo dadi 0xC0, kita entuk kompatibilitas parsial karo CP1252. Ing tembung liya, akeh (nanging ora kabeh) teks Eropa Barat sing dikode ing CP1252 bakal katon padha ing UTF-C.

Nanging ing kene ana kangelan: carane entuk tambahan saka alfabet utama? Sampeyan bisa ninggalake offset sing padha, nanging - sayange - ing kene struktur Unicode wis main nglawan kita. Asring bagean utama alfabet ora ana ing wiwitan blok (contone, ibukutha Rusia "A" duwe kode 0x0410, sanajan pemblokiran Cyrillic wiwit karo 0x0400). Mangkono, kanthi njupuk 64 karakter pisanan menyang stash, kita bisa uga bakal kelangan akses menyang buntut alfabet.

Kanggo ndandani masalah iki, aku kanthi manual ngliwati sawetara blok sing cocog karo macem-macem basa, lan nemtokake offset alfabet tambahan ing sing utama kanggo wong-wong mau. Alfabet Latin, minangka pangecualian, umume disusun maneh kaya base64.

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8

Tutul pungkasan

Pungkasane, kita mikir babagan endi maneh sing bisa nambah.

Elinga yen format 101xxxxx xxxxxxxx xxxxxxxx ngijini sampeyan kanggo encode nomer nganti 0x1FFFFF, lan Unicode ends sadurungé, ing 0x10FFFF. Ing tembung liyane, titik kode pungkasan bakal dituduhake minangka 10110000 11111111 11111111. Mulane, kita bisa ngomong yen byte pisanan saka wangun 1011xxxx (Endi xxxx luwih saka 0), banjur tegese liyane. Contone, sampeyan bisa nambah liyane 15 karakter ana sing terus kasedhiya kanggo enkoding ing siji bait, nanging aku mutusaké kanggo nindakaken beda.

Ayo deleng blok Unicode sing mbutuhake telung bita saiki. Sejatine, kaya sing wis kasebut, iki minangka karakter Cina - nanging angel kanggo nindakake apa wae, ana 21 ewu. Nanging hiragana lan katakana uga mabur ing kana - lan ora akeh maneh, kurang saka rong atus. Lan, amarga kita kelingan karo Jepang, uga ana emojis (nyatane, padha kasebar ing pirang-pirang papan ing Unicode, nanging blok utama ana ing kisaran. 0x1F300 - 0x1FBFF). Yen sampeyan mikir babagan kasunyatan manawa saiki ana emoji sing dirakit saka sawetara titik kode sekaligus (contone, emoji ‍‍‍Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8 kasusun saka minangka akeh minangka 7 kode!), Banjur dadi isin lengkap nglampahi telung bait ing saben (7× 3 = 21 bait kanggo lambang siji, ngipi elek).

Mula, kita milih sawetara kisaran sing dipilih sing cocog karo emoji, hiragana lan katakana, ganti nomer kasebut dadi siji dhaptar terus-terusan lan encode dadi rong bita tinimbang telung:

1011xxxx xxxxxxxx

Apik: emoji sing kasebut ing ndhuwurSepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8, dumadi saka 7 TCTerms kode, njupuk 8 bait ing UTF-25, lan kita pas menyang 14 (persis rong bita kanggo saben titik kode). Miturut cara, Habr ora gelem nyerna (ing editor lawas lan anyar), mula aku kudu nglebokake gambar kasebut.

Ayo dadi nyoba kanggo ndandani siji masalah liyane. Minangka kita elinga, alfabet dhasar iku ateges dhuwur 6 bit, kang kita mbudidaya lan lim kanggo kode saben simbol decoded sabanjuré. Ing kasus karakter Cina sing ana ing blok 0x4E00 - 0x9FFF, iki salah siji dicokot 0 utawa 1. Iki ora trep banget: kita kudu terus-terusan ngalih aksara antarane loro nilai iki (i.e. nglampahi telung bita). Nanging elinga yen ing mode dawa, saka kode kasebut, kita bisa nyuda jumlah karakter sing kita encode nggunakake mode cendhak (sawise kabeh trik kasebut ing ndhuwur, iki 10240) - banjur sawetara hieroglif bakal pindhah menyang 0x2600 - 0x77FF, lan ing kasus iki, ing kabeh kisaran iki, 6 bit sing paling penting (saka 21) bakal padha karo 0. Mangkono, urutan hieroglif bakal nggunakake rong bita saben hieroglif (sing optimal kanggo sawetara gedhe), tanpa nyebabake ngalih aksara.

Solusi alternatif: SCSU, BOCU-1

Pakar Unicode, sing lagi wae maca judhul artikel kasebut, bakal cepet-cepet ngelingake sampeyan manawa ing antarane standar Unicode ana Skema Kompresi Standar kanggo Unicode (SCSU), sing njlèntrèhaké cara enkoding sing meh padha karo sing diterangake ing artikel kasebut.

Aku ngakoni kanthi jujur: Aku sinau babagan orane mung sawise aku nulis keputusanku. Yen aku ngerti babagan iki wiwit wiwitan, aku bakal nyoba nulis implementasine tinimbang teka karo pendekatanku dhewe.

Apa sing menarik yaiku SCSU nggunakake ide sing meh padha karo sing dakkarepake dhewe (tinimbang konsep "abjad" nggunakake "windows", lan luwih akeh sing kasedhiya tinimbang aku). Ing wektu sing padha, format iki uga duwe kekurangan: luwih cedhak karo algoritma kompresi tinimbang enkoding. Utamane, standar menehi akeh cara perwakilan, nanging ora ngomong carane milih sing paling optimal - kanggo iki, encoder kudu nggunakake sawetara jinis heuristik. Mangkono, encoder SCSU sing ngasilake kemasan sing apik bakal luwih rumit lan luwih rumit tinimbang algoritmaku.

Kanggo mbandhingake, aku nransfer implementasine SCSU sing relatif prasaja menyang JavaScript - saka segi volume kode ternyata bisa dibandhingake karo UTF-C, nanging ing sawetara kasus asile puluhan persen luwih elek (kadhangkala bisa ngluwihi, nanging ora akeh). Contone, teks ing basa Ibrani lan Yunani dikode dening UTF-C 60% luwih apik tinimbang SCSU (mbokmenawa amarga aksara sing kompak).

Kapisah, aku bakal nambahake manawa saliyane SCSU, ana uga cara liya kanggo makili Unicode kanthi kompak - BOCU-1, nanging ngarahake kompatibilitas MIME (sing ora dibutuhake) lan njupuk pendekatan sing rada beda kanggo enkoding. Aku wis ora kabiji efektifitas, nanging misale jek kula sing iku dipercaya sing luwih dhuwur tinimbang SCSU.

Bisa dandan

Algoritma sing dakkandhakake ora universal kanthi desain (iki bisa uga tujuanku beda karo tujuan Unicode Consortium). Aku wis nyatakake yen iki dikembangake utamane kanggo siji tugas (nyimpen kamus multibasa ing wit ater-ater), lan sawetara fitur bisa uga ora cocog kanggo tugas liyane. Nanging kasunyatan sing ora standar bisa dadi plus - sampeyan bisa kanthi gampang ngowahi kanggo cocog karo kabutuhan.

Contone, kanthi cara sing jelas sampeyan bisa nyingkirake ngarsane negara, nggawe kode stateless - mung aja nganyari variabel offs, auxOffs и is21Bit ing encoder lan decoder. Ing kasus iki, iku ora bakal bisa kanggo èfèktif Pack urutan karakter saka alfabet padha, nanging bakal ana njamin sing karakter padha tansah dienkode karo bita padha, preduli saka konteks.

Kajaba iku, sampeyan bisa nyetel encoder menyang basa tartamtu kanthi ngganti status standar - contone, fokus ing teks Rusia, nyetel encoder lan decoder ing wiwitan. offs = 0x0400 и auxOffs = 0. Iki utamané ndadekake pangertèn ing cilik saka mode stateless. Umumé, iki bakal padha karo nggunakake enkoding wolung bit lawas, nanging tanpa mbusak kemampuan kanggo nglebokake karakter saka kabeh Unicode yen perlu.

Kelemahan liyane sing kasebut sadurunge yaiku ing teks gedhe sing dikode ing UTF-C ora ana cara cepet kanggo nemokake wates karakter sing paling cedhak karo bait sing sewenang-wenang. Yen sampeyan Cut mati pungkasan, ngandika, 100 bait saka buffer dienkode, sampeyan resiko njaluk uwuh sing ora bisa nindakake apa-apa. Encoding ora dirancang kanggo nyimpen log multi-gigabyte, nanging ing umum iki bisa didandani. Byte 0xBF kudu tau katon minangka byte pisanan (nanging bisa uga kaloro utawa katelu). Mulane, nalika enkoding, sampeyan bisa nglebokake urutan kasebut 0xBF 0xBF 0xBF saben, ngomong, 10 KB - banjur, yen sampeyan kudu golek wates, iku bakal cukup kanggo mindai Piece milih nganti panandha padha ketemu. Dipuntedahaken pungkasan 0xBF dijamin dadi wiwitan karakter. (Nalika dekoding, urutan telung bait iki, mesthine, kudu diabaikan.)

Kanggo ngringkes

Yen sampeyan wis maca nganti saiki, Sugeng! Muga-muga sampeyan, kaya aku, sinau sing anyar (utawa nyegerake memori) babagan struktur Unicode.

Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8
kaca demo. Conto Ibrani nuduhake kaluwihan liwat UTF-8 lan SCSU.

Panliten sing kasebut ing ndhuwur ora kudu dianggep minangka pelanggaran standar. Nanging, aku umume puas karo asil karyaku, mula aku seneng karo dheweke kanggo nuduhake: Contone, perpustakaan JS minified mung 1710 bait (lan ora duwe dependensi, mesthi). Kaya sing kasebut ing ndhuwur, karyane bisa ditemokake ing kaca demo (ana uga sakumpulan teks sing bisa dibandhingake karo UTF-8 lan SCSU).

Pungkasan, aku bakal menehi perhatian maneh babagan kasus sing digunakake UTF-C ora pantes:

  • Yen garis sampeyan cukup dawa (saka 100-200 karakter). Ing kasus iki, sampeyan kudu mikir babagan nggunakake algoritma kompresi kaya deflate.
  • Yen sampeyan perlu Transparansi ASCII, sing, iku penting kanggo sampeyan sing urutan dienkode ora ngemot kode ASCII sing ora ana ing senar asli. Keperluan iki bisa nyingkiri yen, nalika sesambungan karo API pihak katelu (contone, nggarap database), sampeyan ngliwati asil enkoding minangka set abstrak bait, lan ora minangka strings. Yen ora, sampeyan duwe risiko ngalami kerentanan sing ora dikarepake.
  • Yen sampeyan pengin bisa cepet golek wates karakter ing offset kasepakatan (contone, nalika bagean saka baris rusak). Iki bisa ditindakake, nanging mung kanthi mindhai baris saka wiwitan (utawa ngetrapake modifikasi sing diterangake ing bagean sadurunge).
  • Yen sampeyan kudu nindakake operasi kanthi cepet ing isi strings (urutake, goleki substrings ing wong-wong mau, concatenate). Iki mbutuhake strings kanggo decoded pisanan, supaya UTF-C bakal luwih alon saka UTF-8 ing kasus iki (nanging luwih cepet saka algoritma komprèsi). Wiwit string sing padha tansah dikodekake kanthi cara sing padha, perbandingan dekoding sing tepat ora dibutuhake lan bisa ditindakake kanthi basis byte-byte.

nganyari: pangguna Tyomitch ing komentar ing ngisor iki ngirim grafik sing nuduhake watesan aplikasi UTF-C. Iki nuduhake yen UTF-C luwih efisien tinimbang algoritma kompresi tujuan umum (variasi saka LZW) anggere senar sing dikemas luwih cendhek. ~ 140 karakter (Nanging, aku nyathet yen perbandingan kasebut ditindakake ing siji teks; kanggo basa liyane, asil bisa beda-beda).
Sepeda liyane: kita nyimpen senar Unicode 30-60% luwih kompak tinimbang UTF-8

Source: www.habr.com

Add a comment