Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu

Garatzailea bazara eta kodeketa bat aukeratzeko zereginaren aurrean bazaude, Unicode ia beti izango da irtenbide egokia. Irudikapen metodo espezifikoa testuinguruaren araberakoa da, baina gehienetan hemen ere erantzun unibertsala dago - UTF-8. Gauza ona da Unicode karaktere guztiak gastatu gabe erabiltzeko aukera ematen duela gehiegi byte asko kasu gehienetan. Egia da, latindar alfabetoa baino gehiago erabiltzen duten hizkuntzetarako, "ez gehiegi" da gutxienez bi byte karaktere bakoitzeko. Hobetu al dezakegu 256 karaktere erabilgarrietara mugatzen gaituzten historiaurreko kodeketetara itzuli gabe?

Jarraian, galdera honi erantzuteko nire saiakera ezagutzea proposatzen dizut eta munduko hizkuntza gehienetan lerroak gordetzeko aukera ematen duen algoritmo sinple samarra ezartzea UTF-8-n dagoen erredundantzia gehitu gabe.

Erantzukizuna. Berehala erreserba garrantzitsu batzuk egingo ditut: deskribatutako irtenbidea ez da UTF-8ren ordezko unibertsal gisa eskaintzen, kasuen zerrenda estu batean baino ez da egokia (behean gehiago), eta inola ere ez da erabili behar hirugarrenen APIekin (hori buruz ere ez dakiten) elkarreragiteko. Gehienetan, helburu orokorreko konpresio algoritmoak (adibidez, deflate) egokiak dira testu-datu bolumen handiak biltegiratzeko. Horrez gain, jada nire irtenbidea sortzeko prozesuan, Unicode-n lehendik zegoen estandar bat aurkitu nuen, arazo bera konpontzen duena - pixka bat konplikatuagoa (eta askotan okerragoa da), baina oraindik onartutako estandarra da, eta ez bakarrik jarri. elkarrekin belaunean. Berari buruz ere esango dizut.

Unicode eta UTF-8ri buruz

Hasteko, zer den hitz batzuk Unicode ΠΈ UTF-8.

Dakizuenez, 8 biteko kodeketak ezagunak ziren. Horiekin, dena sinplea zen: 256 karaktere zenbakitu daitezke 0tik 255era bitarteko zenbakiekin, eta 0tik 255era bitarteko zenbakiak byte baten moduan irudika daitezke, jakina. Hasierara itzultzen bagara, ASCII kodeketa guztiz mugatuta dago 7 bitetara, beraz, bere byteen irudikapenean bit esanguratsuena zero da, eta 8 biteko kodeketa gehienak bateragarriak dira harekin (Β«goikoΒ»-n soilik desberdintzen dira. zatia, non bit esanguratsuena bat den).

Zertan desberdintzen da Unicode kodeketa horietatik eta zergatik lotzen dira hainbeste irudikapen zehatz harekin - UTF-8, UTF-16 (BE eta LE), UTF-32? Ordena dezagun ordenan.

Oinarrizko Unicode estandarrak karaktereen (eta zenbait kasutan, karaktereen osagai indibidualak) eta haien zenbakien arteko korrespondentzia baino ez du deskribatzen. Eta estandar honetan zenbaki posible asko daude --tik 0x00 to 0x10FFFF (1 pieza). Barruti horretako zenbaki bat aldagai batean jarri nahi bagenu, ez 114 ez 112 byte nahikoa izango liguke. Eta gure prozesadoreak hiru byteko zenbakiekin lan egiteko oso diseinatuta ez daudenez, karaktere bakoitzeko 1 byte erabiltzera behartuta egongo ginateke! Hau UTF-2 da, baina, hain zuzen, "xaferkeria" hori dela eta, formatu hau ez da ezaguna.

Zorionez, Unicode barruan karaktereen ordena ez da ausazkoa. Haien multzo osoa 17"-tan banatuta dagohegazkinak", horietako bakoitzak 65536 (0x10000) "kode puntuak" Hemen "kode puntu" kontzeptua besterik ez da karaktere zenbakia, Unicode-k esleituta. Baina, goian esan bezala, Unicode-n karaktere indibidualak ez ezik, haien osagaiak eta zerbitzu-markak ere zenbatzen dira (eta batzuetan ez dago batere ezer kopuruarekin bat - agian momentuz, baina guretzat hori ez da hain garrantzitsua), beraz zuzenagoa da beti berariaz hitz egitea zenbaki kopuruari buruz, eta ez sinboloei buruz. Hala ere, jarraian, laburtasunaren mesedetan, sarritan β€œsinbolo” hitza erabiliko dut, β€œkode puntua” terminoa inplikatuz.

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu
Unicode planoak. Ikus dezakezunez, gehiena (4tik 13ra bitarteko hegazkinak) oraindik erabili gabe dago.

Azpimarragarriena da "orea" nagusi guztia zero planoan dagoela, " deitzen zaio "Oinarrizko Plano Eleanitza". Lerro batek hizkuntza modernoetako batean (txinera barne) testua badu, ez zara plano honetatik haratago joango. Baina ezin duzu Unicode-ren gainerakoa moztu ere; adibidez, emojiak batez ere amaieran kokatzen dira. hurrengo hegazkina"Plano eleaniztun osagarria"(tik hedatzen da 0x10000 to 0x1FFFF). Beraz, UTF-16k hau egiten du: barruan sartzen diren karaktere guztiak Oinarrizko Plano Eleanitza, "bezala" kodetzen dira, dagokion bi byteko zenbaki batekin. Hala ere, barruti honetako zenbaki batzuek ez dute karaktere zehatzik adierazten, baina byte-pare honen ondoren beste bat kontuan hartu behar dugula adierazten dute; lau byte horien balioak elkarrekin konbinatuz, estaltzen duen zenbaki bat lortzen dugu. Baliozko Unicode sorta osoa. Ideia honi "bikote ordezko" deitzen zaio; baliteke haien berri izatea.

Beraz, UTF-16k bi edo (oso kasu bakanetan) lau byte behar ditu "kode-puntu" bakoitzeko. Hau denbora guztian lau byte erabiltzea baino hobea da, baina latinak (eta beste ASCII karaktere batzuk) horrela kodetuta, espazioaren erdia zeroetan alferrik galtzen du. UTF-8 hau zuzentzeko diseinatuta dago: ASCII horretan, lehen bezala, byte bakarra hartzen du; tik kodeak 0x80 to 0x7FF - bi byte; tik 0x800 to 0xFFFF - hiru, eta 0x10000 to 0x10FFFF - lau. Alde batetik, latindar alfabetoa ona bihurtu da: ASCII-rekin bateragarritasuna itzuli da, eta banaketa berdinago "hedatuta" dago 1etik 4 byteraino. Baina latina ez den alfabetoek, ai, ez dute inola ere onurarik ateratzen UTF-16rekin alderatuta, eta gaur egun askok hiru byte behar dituzte biren ordez - bi byteko erregistro batek hartzen duen tartea 32 aldiz murriztu da, eta 0xFFFF to 0x7FF, eta ez txinera, ez, adibidez, georgiarra ez dira bertan sartzen. Zirilikoa eta beste bost alfabeto - hurra - zortea, 2 byte karaktere bakoitzeko.

Zergatik gertatzen da hau? Ikus dezagun UTF-8-k karaktere kodeak nola adierazten dituen:
Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu
Zuzenean zenbakiak irudikatzeko, ikurrez markatutako bitak erabiltzen dira hemen x. Ikus daiteke bi byteko erregistro batean halako 11 bit besterik ez daudela (16tik). Hemen lehen bitek funtzio laguntzaile bat besterik ez dute. Lau byteko erregistroaren kasuan, 21 bitetatik 32 esleitzen dira kode-puntu-zenbakirako; badirudi hiru byte (guztira 24 bit ematen dituztenak) nahikoa izango liratekeela, baina zerbitzu-markatzaileek gehiegi jaten dute.

Hau txarra al da? Benetan ez. Alde batetik, espazioa asko zaintzen badugu, gain-entropia eta erredundantzia guztiak erraz ezaba ditzaketen konpresio algoritmoak ditugu. Bestalde, Unicoderen helburua ahalik eta kodetze unibertsalena eskaintzea zen. Esate baterako, UTF-8-n kodetutako lerro bat lehen ASCII-rekin bakarrik funtzionatzen zuen kode bat utz diezaiokegu, eta benetan hor ez dagoen ASCII barrutiko karaktere bat ikusiko duen beldurrik gabe (azken finean, UTF-8-n guztiak). zero bitetik hasten diren byteak - hori da ASCII zehazki). Eta bat-batean kate handi bati buztan txiki bat moztu nahi badiogu hasiera-hasieratik deskodetu gabe (edo informazioaren zati bat hondatutako atal baten ondoren berreskuratu), erraza da karaktere bat hasten den desplazamendua aurkitzea (nahikoa da. bit aurrizkia duten byteak saltatzeko 10).

Zergatik asmatu orduan zerbait berria?

Aldi berean, noizean behin, deflate bezalako konpresio algoritmoak gaizki aplikatzen diren egoerak daude, baina kateen biltegiratze trinkoa lortu nahi duzu. Pertsonalki, arazo honekin topo egin nuen eraikitzea pentsatzean aurrizkiaren zuhaitz konprimitua hizkuntza arbitrarioetako hitzak barne dituen hiztegi handi baterako. Alde batetik, hitz bakoitza oso laburra da, beraz, konprimitzea ez da eraginkorra izango. Bestalde, kontuan hartu nuen zuhaitzaren inplementazioa gordetako katearen byte bakoitzak zuhaitz-erpin bereizia sor zezan diseinatu zen, beraz, haien kopurua gutxitzea oso erabilgarria zen. Nire liburutegian Az.js (Bezala pimorfia 2, zeinetan oinarritzen den) antzeko arazo bat besterik gabe konpon daiteke - kateak sartuta Dawg-hiztegi, hor gordeta CP1251 zahar ona. Baina, erraz ulertzen denez, honek ondo funtzionatzen du alfabeto mugatu baterako bakarrik - ezin da txineraz lerro bat gehitu horrelako hiztegi batean.

Bereiz, datu-egitura batean UTF-8 erabiltzean sortzen den Γ±abardura desatsegin bat gehiago adierazi nahi nuke. Goiko irudiak erakusten du karaktere bat bi byte gisa idazten denean, bere zenbakiari lotutako bitak ez direla segidan etortzen, bit pare batek bereizten dituela baizik. 10 erdian: 110xxxxx 10xxxxxx. Hori dela eta, bigarren bytearen beheko 6 bitek karaktere-kodean gainditzen dutenean (hau da, trantsizio bat gertatzen da 10111111 β†’ 10000000), orduan lehenengo bytea ere aldatzen da. Bihurtzen da "p" letra bytez adierazten dela 0xD0 0xBF, eta hurrengo "r"-a dagoeneko da 0xD1 0x80. Aurrizkien zuhaitz batean, honek nodo nagusia bitan zatitzea dakar, bat aurrizkiarentzat. 0xD0, eta beste batentzat 0xD1 (Nahiz eta alfabeto ziriliko osoa bigarren bytearekin soilik kodetu zitekeen).

Zer lortu nuen

Arazo honen aurrean, bitekin jolasten praktikatzea erabaki nuen, eta, aldi berean, Unicode-ren egitura osoa hobeto ezagutzea. Emaitza UTF-C kodeketa formatua ("C" for trinkoa), kode-puntu bakoitzeko 3 byte baino gehiago gastatzen dituena, eta askotan bakarrik gastatzeko aukera ematen dizu byte gehigarri bat kodetutako lerro osorako. Honek ASCII ez diren alfabeto askotan kodeketa hori gertatzen dela dakar UTF-30 baino % 60-8 trinkoagoa.

Kodetze- eta deskodetze-algoritmoen ezarpenaren adibideak aurkeztu ditut formularioan JavaScript eta Go liburutegiak, libreki erabil ditzakezu zure kodean. Baina oraindik ere azpimarratuko dut formatu hau "bizikleta" izaten jarraitzen duela, eta ez dut erabiltzea gomendatzen. zergatik behar duzun konturatu gabe. Hau oraindik esperimentu bat da "UTF-8-ren hobekuntza" serio bat baino. Dena den, hor kodea txukun, zehatz eta labur idatzita dago, iruzkin eta test estaldura ugarirekin.

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu
Testen emaitzak eta UTF-8rekin alderatzea

Nik ere egin nuen demo orria, non algoritmoaren errendimendua ebaluatu dezakezu, eta gero bere printzipioei eta garapen prozesuari buruz gehiago esango dizut.

Bit erredundanteak ezabatzea

UTF-8 hartu nuen oinarritzat, noski. Bertan alda daitekeen lehenengo eta agerikoena byte bakoitzeko zerbitzu-bit kopurua murriztea da. Adibidez, UTF-8ko lehen bytea beti hasten da biekin 0, edo rekin 11 - aurrizki bat 10 Ondoko byteek bakarrik dute. Ordezka dezagun aurrizkia 11 on 1, eta hurrengo byteetarako guztiz kenduko ditugu aurrizkiak. Zer gertatuko da?

0xxxxxxx - 1 byte
10xxxxxx xxxxxxxx - 2 byte
110xxxxx xxxxxxxx xxxxxxxx - 3 byte

Itxaron, non dago lau byteko erregistroa? Baina jada ez da beharrezkoa - hiru bytetan idaztean, orain 21 bit ditugu eskuragarri eta hau nahikoa da zenbaki guztietarako. 0x10FFFF.

Zer sakrifikatu dugu hemen? Garrantzitsuena buffer-eko kokapen arbitrario batetik karaktere-mugak detektatzea da. Ezin dugu byte arbitrario bat apuntatu eta bertatik hurrengo karakterearen hasiera aurkitu. Hau gure formatuaren muga bat da, baina praktikan hori gutxitan beharrezkoa da. Normalean, hasiera-hasieratik buffer-a igarotzeko gai gara (batez ere lerro laburrei dagokienez).

2 byteko hizkuntzak estaltzeko egoera ere hobetu da: orain bi byteko formatuak 14 biteko tartea ematen du, eta hauek arteko kodeak dira. 0x3FFF. Txinakoek zorte txarra dute (beren karaktereak gehienbat 0x4E00 to 0x9FFF), baina georgiarrek eta beste herri askok dibertigarriagoa dute - haien hizkuntzak ere karaktere bakoitzeko 2 bytetan sartzen dira.

Sartu kodetzailearen egoera

Pentsa dezagun orain lerroen propietateetan. Hiztegiak gehienetan alfabeto bereko karaktereekin idatzitako hitzak ditu, eta hori beste testu askotan ere gertatzen da. Ona litzateke alfabeto hau behin adieraztea, eta gero barruan dagoen letraren zenbakia bakarrik adieraztea. Ea Unicode taulako karaktereen antolaketak lagunduko digun.

Goian esan bezala, Unicode zatitan dago hegazkina 65536 kode bakoitzak. Baina hau ez da zatiketa oso erabilgarria (esan bezala, gehienetan zero planoan gaude). Interesgarriagoa da zatiketa blokeak. Barruti hauek jada ez dute luzera finkorik, eta esanguratsuagoak dira; oro har, bakoitzak alfabeto bereko karaktereak konbinatzen ditu.

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu
Bengali alfabetoko karaktereak dituen blokea. Zoritxarrez, arrazoi historikoengatik, hau ontzi ez oso trinkoaren adibidea da - 96 karaktere kaotikoki sakabanatuta daude 128 bloke-kode puntuetan.

Blokeen hasierak eta haien tamainak 16ren multiploak dira beti - erosotasunerako besterik ez da egiten. Gainera, bloke asko 128 edo are 256ren multiploak diren balioetan hasten eta amaitzen dira; adibidez, oinarrizko alfabeto zirilikoak 256 byte hartzen ditu. 0x0400 to 0x04FF. Hau nahiko erosoa da: aurrizkia behin gordetzen badugu 0x04, orduan edozein karaktere ziriliko idatz daiteke byte batean. Egia da, horrela galduko dugu ASCIIra (eta oro har beste edozein pertsonaietara) itzultzeko aukera. Beraz, hau egiten dugu:

  1. Bi byte 10yyyyyy yxxxxxxx zenbaki batekin ikur bat adierazten ez ezik yyyyyy yxxxxxxx, baina baita aldatu ere egungo alfabetoa on yyyyyy y0000000 (hau da, bit guztiak gogoratzen ditugu esanguratsuenak izan ezik 7 bit);
  2. Byte bat 0xxxxxxx hau da egungo alfabetoaren karakterea. 1. urratsean gogoratu dugun desplazamenduari gehitu behar zaio. Alfabetoa aldatu ez dugun arren, desplazamendua zero da, beraz, ASCII-rekin bateragarritasuna mantendu dugu.

Era berean, 3 byte behar dituzten kodeetarako:

  1. Hiru byte 110yyyyy yxxxxxxx xxxxxxxx adierazi ikur bat zenbaki batekin yyyyyy yxxxxxxx xxxxxxxx, aldatu egungo alfabetoa on yyyyyy y0000000 00000000 (guztia gogoratu zen gazteenak izan ezik 15 bit), eta markatu orain gauden laukia luzea modua (alfabetoa byte biko batera aldatzean, bandera hau berrezarri egingo dugu);
  2. Bi byte 0xxxxxxx xxxxxxxx modu luzean uneko alfabetoaren karakterea da. Era berean, 1. urratseko desplazamenduarekin gehitzen dugu. Desberdintasun bakarra da orain bi byte irakurtzen ditugula (modu honetara aldatu garelako).

Ondo dirudi: orain 7 biteko Unicode sorta bereko karaktereak kodetu behar ditugun bitartean, byte gehigarri bat gastatzen dugu hasieran eta karaktere bakoitzeko byte bat guztira.

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu
Aurreko bertsioetako batetik lan egiten. Dagoeneko askotan UTF-8 gainditzen du, baina oraindik badago zer hobetu.

Zer da txarragoa? Lehenik eta behin, baldintza bat dugu, alegia uneko alfabetoaren desplazamendua eta kontrol-laukia modu luzea. Horrek are gehiago mugatzen gaitu: orain karaktere berdinak modu ezberdinean kodetu daitezke testuinguru ezberdinetan. Azpikateen bilaketa, adibidez, hori kontuan hartuta egin beharko da, eta ez byteak konparatuz soilik. Bigarrenik, alfabetoa aldatu bezain laster, txarra bihurtu zen ASCII karaktereen kodeketarekin (eta hau ez da latindar alfabetoa bakarrik, baita oinarrizko puntuazioa ere, zuriuneak barne) - alfabetoa berriro 0ra aldatzea eskatzen dute, hau da, berriro byte gehigarri bat (eta gero beste bat gure puntu nagusira itzultzeko).

Alfabeto bat ona da, bi hobeak

Saia gaitezen gure bit-aurrizkiak pixka bat aldatzen, goian deskribatutako hirurak bat gehiago estutuz:

0xxxxxxx β€” 1 byte modu normalean, 2 modu luzean
11xxxxxx - 1 byte
100xxxxx xxxxxxxx - 2 byte
101xxxxx xxxxxxxx xxxxxxxx - 3 byte

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu

Orain bi byte-ko erregistro batean bit bat gutxiago dago erabilgarri - kode puntuak arte 0x1FFFEta ez 0x3FFF. Hala ere, byte biko UTF-8 kodeetan baino nabarmen handiagoa da oraindik, hizkuntza ohikoenak sartzen dira oraindik, galera nabarmenena jaitsi egin da. hiragana ΠΈ katakana, japoniarrak triste daude.

Zer da kode berri hau? 11xxxxxx? Hau 64 karaktereko tamainako "stash" txiki bat da, gure alfabeto nagusia osatzen du, beraz, laguntzaile deitu nion (osagarriaren) alfabetoa. Egungo alfabetoa aldatzen dugunean, alfabeto zaharraren zati bat laguntzaile bihurtzen da. Esate baterako, ASCIItik zirilikora aldatu ginen - gordelekuak 64 karaktere ditu orain. Latin alfabetoa, zenbakiak, espazioa eta koma (ASCII ez diren testuetan sarrien txertaketak). Itzuli ASCIIra - eta alfabeto zirilikoaren zati nagusia alfabeto laguntzaile bihurtuko da.

Bi alfabetoetarako sarbideari esker, testu ugari maneiatu ditzakegu alfabetoa aldatzeko kostu minimoekin (puntuazioek gehienetan ASCIIra itzultzea ekarriko dute, baina horren ostean ASCII ez diren karaktere asko lortuko ditugu alfabeto gehigarritik, gabe. berriro aldatzea).

Hobaria: azpialfabetoaren aurrizkia jartzea 11xxxxxx eta bere hasierako desplazamendua izatea aukeratzea 0xC0, CP1252-rekin bateragarritasun partziala lortzen dugu. Beste era batera esanda, CP1252n kodetutako mendebaldeko Europako testu askok (baina ez guztiek) itxura bera izango dute UTF-Cn.

Hemen, ordea, zailtasun bat sortzen da: nola lortu laguntzaile bat alfabeto nagusitik? Desplazamendu bera utz dezakezu, baina - ai - hemen Unicode egitura jada gure aurka jokatzen ari da. Askotan alfabetoaren zati nagusia ez dago blokearen hasieran (adibidez, Errusiako "A" hiriburuak kodea du 0x0410, bloke zirilikoa hasten den arren 0x0400). Horrela, lehen 64 karaktereak gordelekuan hartu ondoren, baliteke alfabetoaren buztanerako sarbidea galtzea.

Arazo hau konpontzeko, hizkuntza ezberdinei dagozkien bloke batzuk eskuz pasatu ditut, eta nagusiaren barruan alfabeto laguntzailearen desplazamendua zehaztu dut. Latin alfabetoa, salbuespen gisa, orokorrean oinarri bezala berrantolatzen zen64.

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu

Azken ukituak

Pentsa dezagun azkenean zerbait gehiago non hobetu dezakegun.

Kontuan izan formatua dela 101xxxxx xxxxxxxx xxxxxxxx zenbakiak kodetzeko aukera ematen du 0x1FFFFF, eta Unicode lehenago amaitzen da, orduetan 0x10FFFF. Beste era batera esanda, azken kode-puntua honela irudikatuko da 10110000 11111111 11111111. Beraz, esan dezakegu lehenengo bytea formakoa bada 1011xxxx (Non xxxx 0 baino handiagoa), orduan beste zerbait esan nahi du. Adibidez, beste 15 karaktere gehi ditzakezu bertan, byte batean kodetzeko etengabe eskuragarri daudenak, baina beste era batera egitea erabaki nuen.

Ikus ditzagun orain hiru byte behar dituzten Unicode bloke horiek. Funtsean, esan bezala, karaktere txinatarrak dira, baina zaila da haiekin ezer egitea, 21 mila dira. Baina hiraganak eta katakanak ere hara joan ziren hegan β€”eta jada ez dira hainbeste, berrehun baino gutxiagoβ€”. Eta, japoniarrak gogoratu genituenez, emojiak ere badaude (izan ere, Unicode-ko leku askotan sakabanatuta daude, baina bloke nagusiak tartean daude. 0x1F300 - 0x1FBFF). Pentsatzen baduzu orain kode-puntu ezberdinetatik aldi berean muntatzen diren emojiak daudela (adibidez, emoji ‍‍‍Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu 7 kode baino gehiago ditu!), orduan lotsagarria bihurtzen da bakoitzean hiru byte gastatzea (7Γ—3 = 21 byte ikono baten mesedetan, amesgaizto bat).

Hori dela eta, emoji, hiragana eta katakana-ri dagozkion aukeratutako barruti batzuk hautatzen ditugu, zerrenda jarraitu batean birzenbakitzen ditugu eta bi byte gisa kodetzen ditugu hiru izan beharrean:

1011xxxx xxxxxxxx

Bikaina: aipatutako emojiaBeste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu, 7 kode puntuz osatuta, 8 byte hartzen ditu UTF-25n, eta sartzen dugu 14 (zehazki bi byte kode puntu bakoitzeko). Bide batez, Habrrek uko egin zion digeritzeari (editore zaharrean zein berrian), beraz, argazki batekin txertatu behar izan nuen.

Saia gaitezen beste arazo bat konpontzen. Gogoratzen dugunez, oinarrizko alfabetoa da funtsean 6 bit altua, kontuan izan eta hurrengo deskodetutako sinbolo bakoitzaren kodeari itsatsi egiten dioguna. Blokean dauden txinatar karaktereen kasuan 0x4E00 - 0x9FFF, hau 0 edo 1 bit da. Hau ez da oso erosoa: bi balio horien artean alfabetoa etengabe aldatu beharko dugu (hau da, hiru byte gastatu). Baina kontuan izan modu luzean, kodetik bertatik, modu laburra erabiliz kodetzen dugun karaktere kopurua kendu dezakegula (goian deskribatutako trikimailu guztien ondoren, hau 10240 da) - orduan hieroglifoen barrutia aldatuko da. 0x2600 - 0x77FF, eta kasu honetan, barruti osoan zehar, 6 bit esanguratsuenak (21etik) 0ren berdina izango dira. Horrela, hieroglifo-sekuentziak bi byte erabiliko ditu hieroglifo bakoitzeko (halako tarte handi baterako optimoa dena), gabe alfabeto aldaketak eraginez.

Soluzio alternatiboak: SCSU, BOCU-1

Unicodeko adituek, artikuluaren izenburua irakurri berri dutenez, ziurrenik Unicode estandarren artean zuzenean dagoela gogoraraziko dute. Unicoderako konpresio eskema estandarra (SCSU), artikuluan deskribatutakoaren oso antzeko kodeketa metodo bat deskribatzen duena.

Zintzotasunez aitortzen dut: bere existentziaz nire erabakia idazten sakonki murgilduta egon ondoren bakarrik jakin nuen. Hasieratik jakin izan banu, ziurrenik saiatuko nintzatekeen inplementazio bat idazten nire planteamendu propioa sortu beharrean.

Interesgarria da SCSUk nire kabuz asmatutakoen oso antzeko ideiak erabiltzen dituela ("alfabetoen" kontzeptuaren ordez "leihoak" erabiltzen dituzte, eta nik baino gehiago daude eskuragarri). Aldi berean, formatu honek desabantailak ere baditu: konpresio algoritmoetatik apur bat hurbilago dago kodetzeetatik baino. Batez ere, estandarrak errepresentazio-metodo asko ematen ditu, baina ez du esaten optimoa nola aukeratu; horretarako, kodetzaileak heuristika mota batzuk erabili behar ditu. Horrela, ontzi onak sortzen dituen SCSU kodetzailea nire algoritmoa baino konplexuagoa eta astunagoa izango da.

Konparazio baterako, SCSU-ren inplementazio sinple samarra transferitu nuen JavaScriptra - kode-bolumenari dagokionez nire UTF-C-ren parekoa izan zen, baina kasu batzuetan emaitza ehuneko hamarnaka okerragoa izan zen (batzuetan gainditzea izan daiteke, baina ez asko). Esaterako, hebreerazko eta grekoko testuak UTF-C-k kodetzen zituen SCSU baino %60 hobea (ziurrenik haien alfabeto trinkoengatik).

Bereiz, gehituko dut SCSUz gain Unicode trinkoki irudikatzeko beste modu bat ere badagoela - BOCU-1, baina MIME bateragarritasuna du helburu (behar ez nuena) eta kodetzeko ikuspegi apur bat ezberdina hartzen du. Ez dut bere eraginkortasuna baloratu, baina iruditzen zait nekez izango dela SCSU baino.

Hobekuntza posibleak

Aurkeztu dudan algoritmoa ez da unibertsala diseinuz (horretan da ziurrenik nire helburuak Unicode Partzuergoaren helburuetatik gehien aldentzen direnak). Dagoeneko aipatu dut zeregin baterako garatu zela batez ere (hiztegi eleaniztun bat aurrizki-zuhaitz batean gordetzea), eta baliteke bere ezaugarri batzuk ez izatea beste zereginetarako ondo egokiak. Baina estandarra ez izatea abantaila bat izan daiteke - erraz alda dezakezu zure beharretara egokitzeko.

Adibidez, egoeraren presentzia ken dezakezun modu argian, estaturik gabeko kodeketa egin dezakezu - ez eguneratu aldagaiak. offs, auxOffs ΠΈ is21Bit kodetzailean eta deskodetzailean. Kasu honetan, ezin izango da alfabeto bereko karaktere-sekuentziak modu eraginkorrean paketatzea, baina bermea egongo da karaktere bera beti byte berdinekin kodetuta egotea, testuingurua edozein dela ere.

Horrez gain, kodetzailea hizkuntza zehatz batera egokitu dezakezu egoera lehenetsia aldatuz; adibidez, errusierazko testuetan zentratuz, kodetzailea eta deskodetzailea ezarri hasieran. offs = 0x0400 ΠΈ auxOffs = 0. Honek bereziki zentzua du estaturik gabeko moduaren kasuan. Oro har, zortzi biteko kodeketa zaharra erabiltzearen antzekoa izango da, baina Unicode guztietatik karaktereak behar bezala txertatzeko gaitasuna kendu gabe.

Lehen aipatu dugun beste eragozpen bat da UTF-C-n kodetutako testu handietan ez dagoela modu azkarrik byte arbitrario batetik hurbilen dagoen karaktere-muga aurkitzeko. Kodetutako bufferretik azkena, esate baterako, 100 byte mozten baduzu, ezer egin ezin duzun zaborra jasotzeko arriskua duzu. Kodeketa ez dago gigabyte anitzeko erregistroak gordetzeko diseinatuta, baina orokorrean hori zuzendu daiteke. Byte 0xBF ez da inoiz lehenengo byte gisa agertu behar (baina bigarren edo hirugarren izan daiteke). Hori dela eta, kodetzean, sekuentzia txerta dezakezu 0xBF 0xBF 0xBF bakoitza, esate baterako, 10 KB - orduan, muga bat aurkitu behar baduzu, nahikoa izango da hautatutako pieza eskaneatzea antzeko markatzaile bat aurkitu arte. Azkena jarraituz 0xBF pertsonaia baten hasiera izango dela bermatuta dago. (Deskodetzean, hiru byteko sekuentzia hori, noski, alde batera utzi beharko da.)

Laburbilduz

Honaino irakurri baduzu, zorionak! Espero dut, nik bezala, zerbait berria ikasi izana (edo memoria freskatu izana) Unicoderen egiturari buruz.

Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu
Demo orria. Hebreeraren adibideak UTF-8 eta SCSUren gaineko abantailak erakusten ditu.

Goian deskribatutako ikerketa ez da estandarren gainbeheratzat hartu behar. Hala ere, orokorrean pozik nago nire lanaren emaitzekin, beraz, pozik nago haiekin share: adibidez, txikitutako JS liburutegi batek 1710 byte baino ez ditu pisatzen (eta ez du menpekotasunik, noski). Goian aipatu dudan bezala, bere lana helbidean aurki daiteke demo orria (UTF-8 eta SCSUrekin alderatu daitekeen testu multzo bat ere badago).

Azkenik, berriro ere arreta jarriko dut UTF-C erabiltzen den kasuetan ez merezi:

  • Zure lerroak nahikoa luzeak badira (100-200 karaktere). Kasu honetan, deflate bezalako konpresio algoritmoak erabiltzea pentsatu beharko zenuke.
  • Behar baduzu ASCII gardentasuna, hau da, garrantzitsua da zuretzat kodetutako sekuentziak jatorrizko katean ez zeuden ASCII kodeak ez edukitzea. Horren beharra saihestu daiteke, hirugarrenen APIekin elkarreraginean (adibidez, datu-base batekin lan egitean), kodeketaren emaitza byte-multzo abstraktu gisa pasatzen baduzu, eta ez kate gisa. Bestela, ustekabeko ahuleziak izateko arriskua duzu.
  • Desplazamendu arbitrario batean karaktere-mugak azkar aurkitu nahi badituzu (adibidez, lerro baten zati bat hondatuta dagoenean). Hori egin daiteke, baina lerroa hasieratik eskaneatu (edo aurreko atalean azaldutako aldaketa aplikatuz).
  • Kateen edukiei buruzko eragiketak azkar egin behar badituzu (ordenatu, haietan azpikateak bilatu, kateatu). Horretarako kateak lehenik deskodetu behar dira, beraz, UTF-C UTF-8 baino motelagoa izango da kasu hauetan (baina konpresio algoritmoak baino azkarragoa). Kate bera beti modu berean kodetzen denez, deskodetzearen konparaketa zehatza ez da beharrezkoa eta bytez byte egin daiteke.

Eguneratu: erabiltzaileak Tyomitch beheko iruzkinetan grafiko bat argitaratu du UTF-C-ren aplikazio-mugak nabarmentzen dituena. Erakusten du UTF-C helburu orokorreko konpresio algoritmo bat baino eraginkorragoa dela (LZW-ren aldakuntza) betiere paketaturiko katea laburragoa bada. ~140 karaktere (Hala ere, konparaketa testu batean egin dela ohartzen naiz; beste hizkuntzetarako emaitza desberdina izan daiteke).
Beste bizikleta bat: Unicode kateak UTF-30 baino %60-8 trinkoagoak gordetzen ditugu

Iturria: www.habr.com

Gehitu iruzkin berria