Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8

Ef þú ert verktaki og stendur frammi fyrir því verkefni að velja kóðun, þá mun Unicode næstum alltaf vera rétta lausnin. Sértæk framsetningsaðferð fer eftir samhenginu, en oftast er algilt svar hér líka - UTF-8. Það góða við það er að það gerir þér kleift að nota alla Unicode stafi án þess að eyða of mikið mikið af bætum í flestum tilfellum. Að vísu er „ekki of mikið“ að minnsta kosti fyrir tungumál sem nota meira en bara latneska stafrófið tvö bæti á hvern staf. Getum við gert betur án þess að fara aftur í forsögulegar kóðun sem takmarka okkur við aðeins 256 tiltæka stafi?

Hér að neðan legg ég til að kynna þér tilraun mína til að svara þessari spurningu og innleiða tiltölulega einfalt reiknirit sem gerir þér kleift að geyma línur á flestum tungumálum heimsins án þess að bæta við offramboði sem er í UTF-8.

Fyrirvari. Ég mun strax gera nokkra mikilvæga fyrirvara: lausnin sem lýst er er ekki boðin sem alhliða staðgengill fyrir UTF-8, það hentar aðeins í þröngum lista yfir tilvik (meira um þau hér að neðan), og í engu tilviki ætti það að nota til að hafa samskipti við þriðja aðila API (sem vita ekki einu sinni um það). Algengast er að þjöppunaralgrím fyrir almenna notkun (til dæmis deflate) henta til að geyma mikið magn af textagögnum. Þar að auki, þegar í því ferli að búa til lausnina mína, fann ég núverandi staðal í Unicode sjálfu, sem leysir sama vandamálið - það er nokkuð flóknara (og oft verra), en samt er það viðurkenndur staðall, og ekki bara sett saman á hnénu. Ég skal líka segja þér frá honum.

Um Unicode og UTF-8

Til að byrja með, nokkur orð um hvað það er Unicode и UTF-8.

Eins og þú veist var 8 bita kóðun áður vinsæl. Með þeim var allt einfalt: 256 stafi er hægt að númera með tölum frá 0 til 255 og tölur frá 0 til 255 er augljóslega hægt að tákna sem eitt bæti. Ef við förum aftur til byrjunar, þá er ASCII kóðun algjörlega takmörkuð við 7 bita, þannig að mikilvægasti bitinn í bætaframsetningu þess er núll og flestar 8 bita kóðun eru samhæfðar henni (þær eru aðeins mismunandi í „efri“ hluti, þar sem mikilvægasti bitinn er einn ).

Hvernig er Unicode frábrugðið þessum kóðun og hvers vegna eru svo margar sérstakar framsetningar tengdar því - UTF-8, UTF-16 (BE og LE), UTF-32? Við skulum redda því í röð.

Grunn Unicode staðallinn lýsir aðeins samsvörun milli stafa (og í sumum tilfellum einstakra íhluta stafa) og númera þeirra. Og það eru fullt af mögulegum tölum í þessum staðli - frá 0x00 í 0x10FFFF (1 stykki). Ef við vildum setja tölu á slíku bili inn í breytu myndu hvorki 114 né 112 bæti duga okkur. Og þar sem örgjörvarnir okkar eru ekki sérstaklega hannaðir til að vinna með þriggja bæta tölur, þá neyðumst við til að nota allt að 1 bæti á hvern staf! Þetta er UTF-2, en það er einmitt vegna þessarar „eyðslusemi“ sem þetta snið er ekki vinsælt.

Sem betur fer er röð stafanna í Unicode ekki af handahófi. Allt settið þeirra er skipt í 17"flugvélar", sem hvert um sig inniheldur 65536 (0x10000) «kóðapunkta" Hugmyndin um „kóðapunkt“ hér er einfaldlega stafanúmer, úthlutað því af Unicode. En eins og nefnt er hér að ofan, í Unicode eru ekki aðeins einstakir stafir númeraðir, heldur einnig íhlutir þeirra og þjónustumerki (og stundum samsvarar ekkert númerinu - kannski í bili, en fyrir okkur er þetta ekki svo mikilvægt), svo það er réttara að tala alltaf sérstaklega um fjölda talna sjálfra, en ekki tákn. Hins vegar, í stuttu máli, mun ég oft nota orðið „tákn“, sem gefur til kynna hugtakið „kóðapunktur“.

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8
Unicode flugvélar. Eins og þú sérð er megnið af því (vélar 4 til 13) enn ónotað.

Það sem er merkilegast er að öll aðal „kvoða“ liggur í núllplaninu, það er kallað "Basic fjöltyngt flugvél". Ef lína inniheldur texta á einhverju nútímatungumáli (þar á meðal kínversku), muntu ekki fara út fyrir þetta plan. En þú getur heldur ekki klippt afganginn af Unicode af - til dæmis eru emoji aðallega staðsettir í lok næsta flugvél,“Viðbótar fjöltyngt flugvél"(það nær frá 0x10000 í 0x1FFFF). Svo UTF-16 gerir þetta: allir stafir falla innan Basic fjöltyngt flugvél, eru kóðaðar „eins og er“ með samsvarandi tveggja bæta númeri. Hins vegar gefa sumar tölurnar á þessu bili alls ekki tiltekna stafi, en gefa til kynna að eftir þetta par af bæti þurfum við að íhuga annað - með því að sameina gildi þessara fjögurra bæta saman fáum við tölu sem nær yfir allt gilda Unicode svið. Þessi hugmynd er kölluð „staðgöngupör“ - þú gætir hafa heyrt um þau.

Svo UTF-16 krefst tveggja eða (í mjög sjaldgæfum tilfellum) fjögur bæti fyrir hvern "kóðapunkt". Þetta er betra en að nota fjögur bæti allan tímann, en latína (og aðrir ASCII stafir) þegar þeir eru kóðaðir á þennan hátt eyðir helmingi plásssins í núll. UTF-8 er hannað til að leiðrétta þetta: ASCII í því tekur, eins og áður, aðeins eitt bæti; kóðar frá 0x80 í 0x7FF - tvö bæti; frá 0x800 í 0xFFFF - þrjú, og frá 0x10000 í 0x10FFFF - fjórir. Annars vegar er latneska stafrófið orðið gott: samhæfni við ASCII hefur skilað sér og dreifingin er jafnari „dreifð“ frá 1 til 4 bætum. En önnur stafróf en latneska, því miður, gagnast ekki á nokkurn hátt miðað við UTF-16, og margir þurfa nú þrjú bæti í stað tveggja - sviðið sem tveggja bæta skráning nær yfir hefur minnkað um 32 sinnum, með 0xFFFF í 0x7FF, og hvorki kínverska né til dæmis georgískt eru þar með. Kyrillískt og fimm önnur stafróf - húrra - heppinn, 2 bæti á hvern staf.

Hvers vegna gerist þetta? Við skulum sjá hvernig UTF-8 táknar stafakóða:
Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8
Beint til að tákna tölur eru hér notaðir bitar merktir með tákninu x. Það má sjá að í tveggja bæta skrá eru aðeins 11 slíkir bitar (af 16). Leiðandi bitar hér hafa aðeins aukavirkni. Ef um fjögurra bæta færslu er að ræða er 21 af 32 bitum úthlutað fyrir kóðapunktanúmerið - svo virðist sem þrjú bæti (sem gefa samtals 24 bita) væru nóg, en þjónustumerki éta of mikið upp.

Er þetta vont? Eiginlega ekki. Annars vegar, ef okkur er annt um pláss, höfum við þjöppunaralgrím sem geta auðveldlega útrýmt allri auka óreiðu og offramboði. Aftur á móti var markmið Unicode að bjóða upp á sem alhliða kóðun. Til dæmis getum við falið línu sem er kóðuð í UTF-8 kóða sem áður virkaði aðeins með ASCII, og ekki verið hrædd um að hún sjái staf úr ASCII sviðinu sem er í raun ekki til staðar (enda í UTF-8 allt bæti sem byrja á frá núllbitanum - þetta er nákvæmlega það sem ASCII er). Og ef við viljum skyndilega klippa lítinn hala af stórum streng án þess að afkóða hann alveg frá upphafi (eða endurheimta hluta upplýsinga eftir skemmdan hluta), þá er auðvelt fyrir okkur að finna offsetið þar sem karakter byrjar (það er nóg að sleppa bætum sem hafa smá forskeyti 10).

Af hverju þá að finna upp eitthvað nýtt?

Á sama tíma eru stundum aðstæður þar sem þjöppunaralgrím eins og deflate eiga illa við, en þú vilt ná þéttri geymslu á strengjum. Persónulega lenti ég í þessu vandamáli þegar ég hugsaði um byggingu þjappað forskeyti tré fyrir stóra orðabók sem inniheldur orð á handahófskenndum tungumálum. Annars vegar er hvert orð mjög stutt, svo að þjappa því verður árangurslaust. Á hinn bóginn var tréútfærslan sem ég taldi hönnuð þannig að hvert bæti af geymda strengnum myndaði sérstakan tréhorn, svo að lágmarka fjölda þeirra var mjög gagnlegt. Á bókasafninu mínu Az.js (Eins og í pymorphy2, sem það er byggt á) svipað vandamál er hægt að leysa einfaldlega - strengi pakkað inn í DAWG-orðabók, geymd þar í gamla góða CP1251. En eins og auðvelt er að skilja þá virkar þetta bara vel fyrir takmarkað stafróf - ekki er hægt að bæta línu á kínversku í slíka orðabók.

Sérstaklega vil ég benda á enn einn óþægilegan blæbrigði sem kemur upp þegar UTF-8 er notað í slíkri gagnauppbyggingu. Myndin hér að ofan sýnir að þegar stafur er skrifaður sem tvö bæti koma bitarnir sem tengjast fjölda hans ekki í röð, heldur eru þeir aðskildir með bitapörum 10 í miðjunni: 110xxxxx 10xxxxxx. Vegna þessa, þegar neðri 6 bitar annars bætisins flæða yfir í stafakóðann (þ.e.a.s. umskipti eiga sér stað 1011111110000000), þá breytist fyrsta bæti líka. Það kemur í ljós að bókstafurinn „p“ er táknaður með bætum 0xD0 0xBF, og næsta „r“ er nú þegar 0xD1 0x80. Í forskeytitré leiðir þetta til þess að móðurhnútnum er skipt í tvennt - einn fyrir forskeytið 0xD0, og annar fyrir 0xD1 (þó að allt kýrilíska stafrófið gæti aðeins verið kóðað með öðru bæti).

Hvað fékk ég

Frammi fyrir þessu vandamáli ákvað ég að æfa mig í að spila leiki með bitum og kynnast um leið aðeins betur uppbyggingu Unicode í heild sinni. Niðurstaðan var UTF-C kóðunarsniðið ("C" fyrir samningur), sem eyðir ekki meira en 3 bætum á hvern kóðapunkt og leyfir þér oft aðeins að eyða eitt aukabæti fyrir alla kóðuðu línuna. Þetta leiðir til þess að á mörgum stafrófum sem ekki eru ASCII reynist slík kóðun vera 30-60% þéttari en UTF-8.

Ég hef kynnt dæmi um útfærslu á kóðun og afkóðun reiknirit í formi JavaScript og Go bókasöfn, þú getur frjálslega notað þau í kóðanum þínum. En ég mun samt leggja áherslu á að í vissum skilningi er þetta snið áfram „hjól“ og ég mæli ekki með því að nota það án þess að gera sér grein fyrir hvers vegna þú þarft þess. Þetta er samt meira tilraun en alvarleg „umbót á UTF-8“. Engu að síður er kóðinn þar skrifaður snyrtilegur, hnitmiðaður, með miklum fjölda athugasemda og prófumfjöllun.

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8
Prófunarniðurstöður og samanburður við UTF-8

Ég gerði það líka kynningarsíðu, þar sem þú getur metið árangur reikniritsins, og þá mun ég segja þér meira um meginreglur þess og þróunarferli.

Útrýming óþarfa bita

Ég tók UTF-8 sem grunn, auðvitað. Það fyrsta og augljósasta sem hægt er að breyta í henni er að fækka þjónustubitum í hverju bæti. Til dæmis byrjar fyrsta bætið í UTF-8 alltaf á öðru hvoru 0, eða með 11 - forskeyti 10 Aðeins eftirfarandi bæti hafa það. Skiptum um forskeytið 11 á 1, og fyrir næstu bæti munum við fjarlægja forskeytin alveg. Hvað mun gerast?

0xxxxxxx — 1 bæti
10xxxxxx xxxxxxxx - 2 bæti
110xxxxx xxxxxxxx xxxxxxxx - 3 bæti

Bíddu, hvar er fjögurra bæta skráin? En það er ekki lengur þörf - þegar skrifað er í þremur bætum höfum við nú 21 bita tiltæka og það dugar fyrir allar tölur upp að 0x10FFFF.

Hverju höfum við fórnað hér? Mikilvægast er að greina eðlismörk frá handahófskenndum stað í biðminni. Við getum ekki bent á handahófskennt bæti og fundið upphaf næsta stafa úr því. Þetta er takmörkun á sniði okkar, en í reynd er þetta sjaldan nauðsynlegt. Við erum venjulega fær um að keyra í gegnum biðminni alveg frá upphafi (sérstaklega þegar það kemur að stuttum línum).

Ástandið við að ná yfir tungumál með 2 bætum hefur líka orðið betra: nú gefur tveggja bæta sniðið svið upp á 14 bita, og þetta eru kóðar allt að 0x3FFF. Kínverjar eru óheppnir (persónur þeirra eru að mestu leyti frá 0x4E00 í 0x9FFF), en Georgíumenn og margar aðrar þjóðir skemmta sér betur - tungumál þeirra passa líka inn í 2 bæti á hvern staf.

Sláðu inn kóðarastöðu

Við skulum nú hugsa um eiginleika línanna sjálfra. Orðabókin inniheldur oftast orð sem eru skrifuð með stöfum í sama stafrófinu og það á einnig við um marga aðra texta. Gott væri að tilgreina þetta stafróf einu sinni og þá aðeins númer stafsins í því. Við skulum sjá hvort uppröðun stafa í Unicode töflunni hjálpi okkur.

Eins og getið er hér að ofan er Unicode skipt í flugvél 65536 kóðar hver. En þetta er ekki mjög gagnleg skipting (eins og áður hefur verið sagt, oftast erum við í núllplaninu). Athyglisverðari er skiptingin eftir blokkir. Þessi svið hafa ekki lengur fasta lengd og eru þýðingarmeiri - að jafnaði sameinar hver stafi úr sama stafrófinu.

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8
Kubb sem inniheldur stafi af bengalska stafrófinu. Því miður, af sögulegum ástæðum, er þetta dæmi um ekki mjög þéttar umbúðir - 96 stafir eru óskipulega dreifðir um 128 blokkkóðapunkta.

Upphaf blokka og stærðir þeirra eru alltaf margfeldi af 16 - þetta er gert einfaldlega til þæginda. Að auki byrja og enda margar blokkir á gildum sem eru margfeldi af 128 eða jafnvel 256 - til dæmis tekur grunnkýrilíska stafrófið 256 bæti frá 0x0400 í 0x04FF. Þetta er mjög þægilegt: ef við vistum forskeytið einu sinni 0x04, þá er hægt að skrifa hvaða kýrilíska staf sem er í einu bæti. Satt, þannig munum við missa tækifærið til að fara aftur í ASCII (og til annarra persóna almennt). Þess vegna gerum við þetta:

  1. Tvö bæti 10yyyyyy yxxxxxxx ekki aðeins tákna tákn með tölu yyyyyy yxxxxxxx, en einnig breytast núverandi stafróf á yyyyyy y0000000 (þ.e.a.s. við munum eftir öllum bitunum nema þeim minnstu 7 bita);
  2. Eitt bæti 0xxxxxxx þetta er eðli núverandi stafrófs. Það þarf bara að bæta því við offsetið sem við mundum eftir í skrefi 1. Þó að við breyttum ekki stafrófinu er offsetið núll, þannig að við héldum samhæfni við ASCII.

Sömuleiðis fyrir kóða sem þurfa 3 bæti:

  1. Þrjú bæti 110yyyyy yxxxxxxx xxxxxxxx tilgreina tákn með tölu yyyyyy yxxxxxxx xxxxxxxx, breyta núverandi stafróf á yyyyyy y0000000 00000000 (munaði allt nema þau yngri 15 bita), og hakaðu í reitinn sem við erum núna í Langt ham (þegar stafrófinu er breytt aftur í tvöfalt bæti, munum við endurstilla þennan fána);
  2. Tvö bæti 0xxxxxxx xxxxxxxx í langri stillingu er það stafur núverandi stafrófs. Á sama hátt bætum við því við með offsetinu frá skrefi 1. Eini munurinn er sá að nú lesum við tvö bæti (vegna þess að við skiptum yfir í þennan ham).

Hljómar vel: núna á meðan við þurfum að umrita stafi úr sama 7-bita Unicode-sviðinu, þá eyðum við 1 aukabæti í upphafi og samtals einu bæti á hvern staf.

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8
Að vinna úr einni af fyrri útgáfum. Það slær oft út UTF-8, en það er enn pláss fyrir umbætur.

Hvað er verra? Í fyrsta lagi höfum við skilyrði, þ.e núverandi stafrófsjöfnun og gátreit langur hamur. Þetta takmarkar okkur enn frekar: nú er hægt að kóða sömu stafi á annan hátt í mismunandi samhengi. Leita að undirstrengjum, til dæmis, verður að gera með hliðsjón af þessu, en ekki bara með því að bera saman bæti. Í öðru lagi, um leið og við breyttum stafrófinu, varð það slæmt með kóðun ASCII stafa (og þetta er ekki aðeins latneska stafrófið, heldur einnig grunn greinarmerki, þar á meðal bil) - þeir þurfa að breyta stafrófinu aftur í 0, það er, aftur auka bæti (og svo annað til að komast aftur að aðalatriðinu okkar).

Eitt stafróf er gott, tvö er betra

Við skulum reyna að breyta bitaforskeytum okkar aðeins, kreista inn eitt í viðbót af þremur sem lýst er hér að ofan:

0xxxxxxx — 1 bæti í venjulegri stillingu, 2 í langri stillingu
11xxxxxx — 1 bæti
100xxxxx xxxxxxxx - 2 bæti
101xxxxx xxxxxxxx xxxxxxxx - 3 bæti

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8

Nú í tveggja bæta skrá er einum minna tiltækum bita - kóða bendir upp á 0x1FFFOg ekki 0x3FFF. Hins vegar er það enn áberandi stærra en í tvíbæta UTF-8 kóða, algengustu tungumálin passa enn inn, mest áberandi tapið hefur fallið út hiragana и katakana, Japanir eru sorgmæddir.

Hvað er þessi nýi kóði? 11xxxxxx? Þetta er lítið „geymsla“ með 64 stöfum að stærð, það bætir við aðalstafrófið okkar, svo ég kallaði það aukastaf (tengd) stafrófið. Þegar við skiptum um núverandi stafróf verður hluti af gamla stafrófinu aukastafur. Við skiptum til dæmis úr ASCII yfir í kyrillísku - geymslan inniheldur nú 64 stafi sem innihalda Latneskt stafróf, tölur, bil og kommu (algengasta innskot í texta sem ekki eru ASCII). Skiptu aftur í ASCII - og meginhluti kyrillíska stafrófsins verður aukastafrófið.

Þökk sé aðgangi að tveimur stafrófum getum við séð um mikinn fjölda texta með lágmarkskostnaði við að skipta um stafróf (greinarmerki mun oftast leiða til þess að fara aftur í ASCII, en eftir það fáum við marga stafi sem ekki eru ASCII úr viðbótarstafrófinu, án skipta aftur).

Bónus: forskeyti undirstafrófsins 11xxxxxx og velja upphafsjöfnun þess 0xC0, fáum við samhæfni að hluta við CP1252. Með öðrum orðum, margir (en ekki allir) vestur-evrópskir textar sem eru umritaðir í CP1252 munu líta eins út í UTF-C.

Hér koma hins vegar upp erfiðleikar: hvernig á að fá auka úr aðalstafrófinu? Þú getur skilið eftir sama mótvægi, en - því miður - hér er Unicode uppbyggingin þegar að spila gegn okkur. Mjög oft er meginhluti stafrófsins ekki í byrjun reitsins (td er rússneska höfuðborgin „A“ með kóðann 0x0410, þó kyrillíska blokkin byrji á 0x0400). Þannig, eftir að hafa tekið fyrstu 64 stafina í geymsluna, gætum við misst aðgang að skotthluta stafrófsins.

Til að laga þetta vandamál fór ég handvirkt í gegnum nokkrar blokkir sem samsvara mismunandi tungumálum og tilgreindi frávik aukastafrófsins innan aðalstafrófsins fyrir þá. Latneska stafrófið, sem undantekning, var almennt endurraðað eins og base64.

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8

Lokaatriði

Við skulum loksins hugsa um hvar annað við getum bætt eitthvað.

Athugið að sniðið 101xxxxx xxxxxxxx xxxxxxxx gerir þér kleift að kóða tölur allt að 0x1FFFFF, og Unicode lýkur fyrr, kl 0x10FFFF. Með öðrum orðum, síðasta kóðapunkturinn verður táknaður sem 10110000 11111111 11111111. Þess vegna getum við sagt að ef fyrsta bæti er af forminu 1011xxxx (Hvar xxxx stærri en 0), þá þýðir það eitthvað annað. Þar er til dæmis hægt að bæta við 15 stöfum í viðbót sem eru stöðugt í boði fyrir kóðun í einu bæti, en ég ákvað að gera það öðruvísi.

Við skulum skoða þessar Unicode blokkir sem þurfa þrjú bæti núna. Í grundvallaratriðum, eins og áður hefur verið nefnt, eru þetta kínverskir stafir - en það er erfitt að gera neitt við þá, þeir eru 21 þúsund. En þangað flugu líka hiragana og katakana - og þau eru ekki svo mörg lengur, innan við tvö hundruð. Og þar sem við mundum eftir japönskum, þá eru líka emojis (reyndar eru þeir á víð og dreif í Unicode, en aðalblokkirnar eru á bilinu 0x1F300 - 0x1FBFF). Ef þú hugsar um þá staðreynd að nú eru til emojis sem eru sett saman úr nokkrum kóðapunktum í einu (td emoji ‍‍‍Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8 samanstendur af allt að 7 kóðum!), þá verður það algjör synd að eyða þremur bætum í hvern (7×3 = 21 bæti vegna eins tákns, martröð).

Þess vegna veljum við nokkur valin svið sem samsvara emoji, hiragana og katakana, endurnúmerum þau í einn samfelldan lista og kóðum þau sem tvö bæti í stað þriggja:

1011xxxx xxxxxxxx

Frábært: áðurnefnt emojiAnnað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8, sem samanstendur af 7 kóðapunktum, tekur 8 bæti í UTF-25 og við pössum það inn 14 (nákvæmlega tvö bæti fyrir hvern kóðapunkt). Við the vegur, Habr neitaði að melta það (bæði í gamla og nýja ritstjóranum), svo ég varð að setja það inn með mynd.

Við skulum reyna að laga enn eitt vandamálið. Eins og við munum er grunnstafrófið í meginatriðum hár 6 bitar, sem við höfum í huga og límum við kóða hvers næsta afkóðaða tákns. Ef um er að ræða kínverska stafi sem eru í blokkinni 0x4E00 - 0x9FFF, þetta er annað hvort biti 0 eða 1. Þetta er ekki mjög þægilegt: við þurfum stöðugt að skipta stafrófinu á milli þessara tveggja gilda (þ.e. eyða þremur bætum). En athugaðu að í langa stillingunni, frá kóðanum sjálfum, getum við dregið fjölda stafa sem við kóðum með því að nota stutta stillinguna (eftir öll brellurnar sem lýst er hér að ofan, þetta er 10240) - þá mun svið myndmerkja breytast í 0x2600 - 0x77FF, og í þessu tilfelli, á öllu þessu sviði, munu mikilvægustu 6 bitarnir (af 21) vera jafnir 0. Þannig munu raðir af myndlistum nota tvö bæti á hvert myndmerki (sem er ákjósanlegt fyrir svo stórt svið), án þess að veldur stafrófsskiptum.

Aðrar lausnir: SCSU, BOCU-1

Sérfræðingar Unicode, sem nýlega hafa lesið titil greinarinnar, munu líklegast flýta sér að minna þig á að beint meðal Unicode staðla er Hefðbundið þjöppunarkerfi fyrir Unicode (SCSU), sem lýsir kóðun aðferð sem er mjög svipuð þeirri sem lýst er í greininni.

Ég viðurkenni það hreinskilnislega: Ég lærði um tilvist þess aðeins eftir að ég var djúpt á kafi í að skrifa ákvörðun mína. Hefði ég vitað af því frá upphafi hefði ég líklega reynt að skrifa útfærslu í stað þess að koma með mína eigin nálgun.

Það sem er athyglisvert er að SCSU notar hugmyndir sem eru mjög svipaðar þeim sem ég fann upp á eigin spýtur (í stað hugtaksins „stafróf“ nota þau „glugga“ og það eru fleiri af þeim tiltækar en ég hef). Á sama tíma hefur þetta snið einnig ókosti: það er aðeins nær þjöppunaralgrímum en kóðun. Einkum gefur staðallinn margar framsetningaraðferðir, en segir ekki hvernig á að velja ákjósanlegasta - til þess verður kóðarinn að nota einhvers konar heuristics. Þannig mun SCSU kóðari sem framleiðir góðar umbúðir verða flóknari og fyrirferðarmeiri en reikniritið mitt.

Til samanburðar flutti ég tiltölulega einfalda útfærslu á SCSU yfir á JavaScript - hvað varðar kóðamagn reyndist það sambærilegt við UTF-C minn, en í sumum tilfellum var útkoman tugum prósenta verri (stundum gæti hún farið yfir það, en ekki mikið). Til dæmis voru textar á hebresku og grísku kóðaðir með UTF-C 60% betri en SCSU (sennilega vegna þéttra stafrófanna).

Sérstaklega bæti ég því við að fyrir utan SCSU er líka önnur leið til að tákna Unicode - BOCU-1, en það miðar að MIME samhæfni (sem ég þurfti ekki) og tekur aðeins aðra nálgun við kóðun. Ég hef ekki metið virkni þess, en mér sýnist að það sé ólíklegt að það sé hærra en SCSU.

Mögulegar úrbætur

Reikniritið sem ég kynnti er ekki algilt í hönnun (þetta er líklega þar sem markmiðin mín víkja mest frá markmiðum Unicode Consortium). Ég hef þegar nefnt að það var fyrst og fremst þróað fyrir eitt verkefni (geyma fjöltyngda orðabók í forskeytitré), og sumir eiginleikar þess gætu ekki hentað vel fyrir önnur verkefni. En sú staðreynd að það er ekki staðall getur verið plús - þú getur auðveldlega breytt því til að henta þínum þörfum.

Til dæmis, á augljósan hátt geturðu losað þig við tilvist ríkis, búið til ríkisfangslausa kóðun - bara ekki uppfæra breytur offs, auxOffs и is21Bit í kóðara og afkóðara. Í þessu tilviki verður ekki hægt að pakka í raun saman raðir stafa af sama stafrófinu, en það verður tryggt að sami stafurinn sé alltaf kóðaður með sömu bætum, óháð samhengi.

Að auki geturðu sérsniðið kóðann að tilteknu tungumáli með því að breyta sjálfgefna stöðunni - til dæmis með því að einbeita þér að rússneskum texta, stilla kóðara og afkóðara í upphafi offs = 0x0400 и auxOffs = 0. Þetta er sérstaklega skynsamlegt þegar um ríkisfangslausan hátt er að ræða. Almennt séð mun þetta vera svipað og að nota gamla átta bita kóðun, en án þess að fjarlægja möguleikann á að setja inn stafi úr öllum Unicode eftir þörfum.

Annar galli sem nefndur var áðan er að í stórum texta sem er umritaður í UTF-C er engin fljótleg leið til að finna stafamörkin næst handahófskenndu bæti. Ef þú klippir síðustu, segjum, 100 bæti af kóðuðu biðminni, er hætta á að þú fáir rusl sem þú getur ekki gert neitt með. Kóðunin er ekki hönnuð til að geyma margra gígabæta annála, en almennt er hægt að leiðrétta þetta. Bæti 0xBF má aldrei birtast sem fyrsta bæti (en getur verið annað eða þriðja). Þess vegna, þegar þú kóðar, geturðu sett inn röðina 0xBF 0xBF 0xBF hvert, segjum, 10 KB - þá, ef þú þarft að finna mörk, mun það vera nóg að skanna valið verk þar til svipað merki finnst. Eftir sl 0xBF er ábyrg fyrir að vera upphaf persóna. (Við afkóðun þarf auðvitað að hunsa þessa röð af þremur bætum.)

Toppur upp

Ef þú hefur lesið þetta langt, til hamingju! Ég vona að þú, eins og ég, hafið lært eitthvað nýtt (eða endurnært minni þitt) um uppbyggingu Unicode.

Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8
Demo síða. Dæmið um hebresku sýnir kosti bæði UTF-8 og SCSU.

Ofangreindar rannsóknir ættu ekki að teljast inngrip í staðla. Hins vegar er ég almennt ánægður með árangur vinnu minnar og er því ánægður með þær deila: til dæmis, smækkað JS bókasafn vegur aðeins 1710 bæti (og hefur auðvitað engin ósjálfstæði). Eins og ég nefndi hér að ofan er verk hennar að finna á kynningarsíðu (það er líka sett af textum sem hægt er að bera það saman við UTF-8 og SCSU).

Að lokum mun ég enn og aftur vekja athygli á tilfellum þar sem UTF-C er notað ekki þess virði:

  • Ef línurnar þínar eru nógu langar (frá 100-200 stöfum). Í þessu tilfelli ættir þú að hugsa um að nota þjöppunaralgrím eins og deflate.
  • Ef þú þarft ASCII gagnsæi, það er, það er mikilvægt fyrir þig að kóðuðu raðirnar innihaldi ekki ASCII kóða sem voru ekki í upprunalega strengnum. Hægt er að forðast þörfina fyrir þetta ef, þegar þú hefur samskipti við þriðja aðila API (til dæmis, þegar þú vinnur með gagnagrunn), þú sendir kóðunarniðurstöðuna sem óhlutbundið sett af bætum, en ekki sem strengi. Annars er hætta á að þú fáir óvænta veikleika.
  • Ef þú vilt vera fær um að finna mörk stafs fljótt með handahófskenndri mótvægi (til dæmis þegar hluti línu er skemmdur). Þetta er hægt að gera, en aðeins með því að skanna línuna frá upphafi (eða beita breytingunni sem lýst er í fyrri hlutanum).
  • Ef þú þarft að framkvæma fljótt aðgerðir á innihaldi strengja (raða þeim, leita að undirstrengjum í þeim, sameina). Þetta krefst þess að strengir séu afkóðaðir fyrst, þannig að UTF-C verður hægari en UTF-8 í þessum tilvikum (en hraðari en þjöppunaralgrím). Þar sem sami strengurinn er alltaf kóðaður á sama hátt, er ekki þörf á nákvæmum samanburði á umskráningu og hægt er að gera það á bæti fyrir bæti.

Update: notandi Tyomitch í athugasemdum hér að neðan birti línurit sem undirstrikar nothæfismörk UTF-C. Það sýnir að UTF-C er skilvirkara en almennt þjöppunaralgrím (afbrigði af LZW) svo framarlega sem pakkaði strengurinn er styttri ~140 stafir (Ég tek hins vegar fram að samanburðurinn var gerður á einum texta; fyrir önnur tungumál getur niðurstaðan verið önnur).
Annað hjól: við geymum Unicode strengi sem eru 30-60% þéttari en UTF-8

Heimild: www.habr.com

Bæta við athugasemd