En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8

Wann Dir en EntwĂ©ckler sidd an Dir sidd mat der Aufgab konfrontĂ©iert fir eng KodĂ©ierung ze wielen, da wĂ€ert Unicode bal Ă«mmer dĂ©i richteg LĂ©isung sinn. DĂ©i spezifesch Representatiounsmethod hĂ€nkt vum Kontext of, awer meeschtens gĂ«tt et och hei eng universell Äntwert - UTF-8. DĂ©i gutt Saach doriwwer ass datt et Iech erlaabt all Unicode Charaktere ze benotzen ouni Ausgaben ze vill vill Bytes am meeschte FĂ€ll. Richteg, fir Sproochen dĂ©i mĂ©i wĂ©i just dat latĂ©ngescht Alphabet benotzen, ass "net zevill" op d'mannst zwee Bytes pro Charakter. KĂ«nne mir et besser maachen ouni op prehistoresch KodĂ©ierungen zrĂ©ckzekommen, dĂ©i eis op just 256 verfĂŒgbare Charaktere limitĂ©ieren?

Drënner proposéieren ech Iech mat mengem Versuch vertraut ze maachen dës Fro ze beÀntweren an e relativ einfachen Algorithmus ëmzesetzen deen Iech erlaabt Linnen an de meeschte Sprooche vun der Welt ze spÀicheren ouni d'Redundanz ze addéieren déi am UTF-8 ass.

VerzichterklÀrung. Ech wÀert direkt e puer wichteg Reservatioune maachen: Déi beschriwwe Léisung gëtt net als universell Ersatz fir UTF-8 ugebueden, et ass nëmme gëeegent an enger schmueler Lëscht vu FÀll (méi iwwer hinnen hei ënnen), an op kee Fall sollt et benotzt ginn fir mat Drëtt Partei APIen ze interagéieren (déi net emol doriwwer wëssen). Meeschtens sinn allgemeng Zwecker Kompressiounsalgorithmen (zum Beispill deflatéieren) gëeegent fir kompakt SpÀichere vu grousse Volumen vun Textdaten. ZousÀtzlech, schonn am Prozess vu menger Léisung ze kreéieren, hunn ech en existente Standard an Unicode selwer fonnt, deen dee selwechte Problem léist - et ass e bësse méi komplizéiert (an dacks méi schlëmm), awer et ass ëmmer nach en akzeptéierte Standard, an net nëmmen gesat. zesummen um Knéi. Ech soen Iech och iwwer hien.

Iwwer Unicode an UTF-8

Fir unzefÀnken, e puer Wierder iwwer wat et ass Unicode О UTF-8.

Wéi Dir wësst, waren 8-Bit Kodéierunge fréier populÀr. Mat hinnen war alles einfach: 256 Zeeche kënne mat Zuelen vun 0 bis 255 nummeréiert ginn, an Zuele vun 0 bis 255 kënnen selbstverstÀndlech als ee Byte duergestallt ginn. Wa mir zréck an den Ufank zréckgoen, ass d'ASCII Kodéierung komplett op 7 Bits limitéiert, sou datt de bedeitendste Bit a senger Byte Representatioun null ass, an déi meescht 8-Bit Kodéierunge si kompatibel mat et (se ënnerscheeden nëmmen am "Uewer" Deel, wou de bedeitendsten bëssen een ass).

Wéi ënnerscheet sech Unicode vun dëse Kodéierungen a firwat si sou vill spezifesch Representatioune mat him verbonnen - UTF-8, UTF-16 (BE an LE), UTF-32? Loosst eis et an Uerdnung sortéieren.

De Basis Unicode Standard beschreift nëmmen d'Korrespondenz tëscht Charakteren (an a verschiddene FÀll eenzel Komponente vun Zeechen) an hiren Zuelen. An et gi vill vun méiglech Zuelen an dësem Standard - vun 0x00 ze 0x10FFFF (1 Stécker). Wa mir eng Nummer an esou engem BerÀich an eng Variabel wëllen setzen, da wiere weder 114 nach 112 Bytes fir eis duer. A well eis Prozessoren net ganz entwéckelt sinn fir mat drÀi-Byte Zuelen ze schaffen, wÀerte mir gezwongen sinn esou vill wéi 1 Bytes pro Charakter ze benotzen! Dëst ass UTF-2, awer et ass genee wéinst dëser "Verschwendung" datt dëst Format net populÀr ass.

Glécklecherweis ass d'Uerdnung vun de Charaktere bannent Unicode net zoufÀlleg. Hire ganze Set ass opgedeelt an 17 "Fligeren", jidderee vun deenen enthÀlt 65536 (0x10000) "Code Punkten" D'Konzept vun engem "Code Punkt" hei ass einfach Zeechen Zuel, vun Unicode zougewisen. Awer, wéi uewen ernimmt, an Unicode sinn net nëmmen eenzel Charaktere nummeréiert, awer och hir Komponenten a Servicemarken (an heiansdo entsprécht nÀischt der Zuel - vlÀicht fir de Moment, awer fir eis ass dat net sou wichteg), also et ass méi richteg schwÀtzen ëmmer speziell iwwert d'Zuel vun Zuelen selwer, an net Symboler. Wéi och ëmmer, am folgenden, fir d'Kuerzlechkeet, wÀert ech dacks d'Wuert "Symbol" benotzen, wat de Begrëff "Codepunkt" implizéiert.

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8
Unicode Fligeren. Wéi Dir gesitt, ass de gréissten Deel (Fliger 4 bis 13) nach net benotzt.

Wat am meeschte bemierkenswĂ€ert ass ass datt all d'Haapt "Pulp" am Nullplang lĂ€it, et gĂ«tt genannt "Basis Multilingual Fliger". Wann eng Zeil Text an enger vun de modernen Sproochen enthĂ€lt (och Chinesesch), wĂ€ert Dir net iwwer dĂ«se Fliger goen. Awer Dir kĂ«nnt och de Rescht vun Unicode net ofschneiden - zum Beispill Emoji sinn haaptsĂ€chlech um Enn vum den nĂ€chste Fliger,"ZousĂ€tzlech Multilingual Plane"(et geet aus 0x10000 ze 0x1FFFF). Also UTF-16 mĂ©cht dĂ«st: all Charaktere falen dobannen Basis Multilingual Fliger, sinn kodĂ©iert "wĂ©i ass" mat enger entspriechender zwee-Byte Zuel. WĂ©i och Ă«mmer, e puer vun den Zuelen an dĂ«sem BerĂ€ich weisen guer net spezifesch Zeechen un, awer weisen datt no dĂ«sem Pair vu Bytes mir eng aner musse berĂŒcksichtegen - andeems Dir d'WĂ€erter vun dĂ«se vĂ©ier Bytes zesumme kombinĂ©iere krĂ©ie mir eng Zuel dĂ©i deckt dĂ©i ganz gĂ«lteg Unicode Gamme. DĂ«s Iddi gĂ«tt "Surrogat Koppelen" genannt - Dir hutt vlĂ€icht vun hinnen hĂ©ieren.

Also UTF-16 erfuerdert zwee oder (a ganz rare FÀll) véier Bytes pro "Codepunkt". Dëst ass besser wéi véier Bytes déi ganzen ZÀit ze benotzen, awer Laténgesch (an aner ASCII Zeechen) wann se op dës Manéier kodéiert verschwenden d'Halschent vum Raum op Nullen. UTF-8 ass entwéckelt fir dëst ze korrigéieren: ASCII an et besetzt, wéi virdrun, nëmmen ee Byte; Coden vun 0x80 ze 0x7FF - zwee Bytes; vun 0x800 ze 0xFFFF - drÀi, an aus 0x10000 ze 0x10FFFF - véier. EngersÀits ass d'laténgesch Alphabet gutt ginn: Kompatibilitéit mat ASCII ass zréck, an d'Verdeelung ass méi glÀichméisseg "verbreet" vun 1 bis 4 Bytes. Awer aner Alphabeten wéi LatÀin, leider, profitéieren op kee Fall am Verglach zum UTF-16, a vill erfuerderen elo drÀi Bytes anstatt zwee - d'Gamme, déi vun engem Zwee-Byte-Rekord bedeckt ass, ass ëm 32 Mol verréngert, mat 0xFFFF ze 0x7FF, a weder Chinesesch nach, zum Beispill, Georgian sinn derbÀi. Kyrillesch a fënnef aner Alphabeten - Hurra - Gléck, 2 Bytes pro Charakter.

Firwat geschitt dat? Loosst eis kucken wéi UTF-8 Charaktercodes duerstellt:
En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8
Direkt fir Zuelen ze representéieren, Bits markéiert mam Symbol ginn hei benotzt x. Et kann gesi ginn datt an engem Zwee-Byte-Rekord nëmmen 11 esou Bits sinn (vun 16). Déi féierend Bits hunn hei nëmmen eng Hëllefsfunktioun. Am Fall vun engem Véier-Byte-Rekord ginn 21 vun 32 Bits fir d'Codepunktnummer zougewisen - et géif schéngen datt drÀi Bytes (déi am Ganzen 24 Bits ginn) genuch wieren, awer Servicemarker iessen ze vill.

Ass dëst schlecht? Net wierklech. EngersÀits, wa mir eis vill ëm de Raum këmmeren, hu mir Kompressiounsalgorithmen déi all extra Entropie a Redundanz ganz einfach eliminéiere kënnen. Op der anerer SÀit war d'Zil vum Unicode fir déi universellst méiglech Kodéierung ze bidden. Zum Beispill kënne mir eng Zeil, déi an UTF-8 encodéiert ass, uvertrauen, fir ze codéieren, déi virdru nëmme mat ASCII geschafft huet, an net Angscht datt et e Charakter aus der ASCII-Bereich wÀert gesinn, déi tatsÀchlech net do ass (schliisslech an UTF-8 alles Bytes ugefaange mat dem Nullbit - dat ass genau wat ASCII ass). A wa mir op eemol e klenge Schwanz vun engem grousse String wëllen ofschneiden ouni et vun Ufank un ze decodéieren (oder en Deel vun der Informatioun no enger beschiedegter Sektioun restauréieren), ass et einfach fir eis de Offset ze fannen wou e Charakter ufÀnkt (et geet duer fir Bytes ze sprangen déi e bësse PrÀfix hunn 10).

Firwat dann eppes Neies erfannen?

Zur selwechter ZĂ€it ginn et heiansdo Situatiounen wou Kompressiounsalgorithmen wĂ©i Deflate schlecht applicabel sinn, awer Dir wĂ«llt kompakt SpĂ€ichere vu Saiten erreechen. PersĂ©inlech hunn ech dĂ«se Problem begĂ©int wann ech iwwer d'Gebai denken komprimĂ©iert PrĂ€fix Bam fir e grousst Wierderbuch abegraff Wierder an arbitrĂ€r Sproochen. EngersĂ€its ass all Wuert ganz kuerz, sou datt et net effikass ass ze komprimĂ©ieren. Op der anerer SĂ€it ass d'Bamimplementatioun, dĂ©i ech betruecht hunn, sou entworf datt all Byte vun der gespĂ€ichert String e separaten Bamvertex generĂ©iert, sou datt hir Zuel minimĂ©iert war ganz nĂ«tzlech. A menger BibliothĂ©ik Az.js (WĂ©i an pymorphy 2, op deem et basĂ©iert) kann en Ă€hnleche Problem einfach gelĂ©ist ginn - Strings gepackt an DAWG-Wörterbuch, do gespĂ€ichert an gutt al CP1251. Awer, wĂ©i einfach ze verstoen ass, funktionnĂ©iert dat nĂ«mme fir e limitĂ©ierten Alphabet - eng Zeil op Chinesesch kann net an esou engem Wierderbuch bĂ€igefĂŒĂŒgt ginn.

Separat wĂ«ll ech eng mĂ©i dĂ©sagrĂ©abel Nuance notĂ©ieren, dĂ©i entsteet wann Dir UTF-8 an esou enger Datestruktur benotzt. D'Bild hei uewen weist datt wann e Charakter als zwee Bytes geschriwwe gĂ«tt, d'Bits am Zesummenhang mat senger Nummer net an enger Zeil kommen, mee duerch e Paar Bits getrennt sinn. 10 an der MĂ«tt: 110xxxxx 10xxxxxx. Dofir, wann dĂ©i Ă«nnescht 6 Bits vum zweete Byte am Zeechecode iwwerflĂ©ien (dh en Iwwergang geschitt 10111111 → 10000000), da Ă€nnert sech och den Ă©ischte Byte. Et stellt sech eraus datt de BrĂ©if "p" mat Bytes bezeechent gĂ«tt 0xD0 0xBF, an den nĂ€chsten "r" ass schonn 0xD1 0x80. An engem PrĂ€fix Bam fĂ©iert dat zu der Spaltung vun der Elterendeel Node an zwee - eent fir de PrĂ€fix 0xD0, an eng aner fir 0xD1 (obwuel dat ganzt kyrillescht Alphabet nĂ«mme vum zweete Byte kodĂ©iert ka ginn).

Wat krut ech

Géint dësem Problem hunn ech beschloss, Spiller mat Bits ze spillen, a glÀichzÀiteg e bësse besser mat der Struktur vum Unicode als Ganzt kennenzeléieren. D'Resultat war den UTF-C Kodéierungsformat ("C" fir kompakt), déi verbréngt net méi wéi 3 Bytes pro Code Punkt, a ganz oft erlaabt Iech nëmmen ze verbréngen een extra Byte fir déi ganz kodéiert Linn. Dëst féiert zu der Tatsaach, datt op villen net-ASCII Alfabeten esou Kodéierung erausstellt 30-60% méi kompakt wéi UTF-8.

Ech hunn Beispiller vun der Ëmsetzung vun KodĂ©ierung an DekodĂ©ierung Algorithmen an der Form presentĂ©iert JavaScript a Go BibliothĂ©iken, Dir kĂ«nnt se frĂ€i an Ärem Code benotzen. Awer ech wĂ€ert nach Ă«mmer Ă«nnerstrĂ€ichen datt dĂ«st Format an engem SĂ«nn e "VĂ«lo" bleift, an ech recommandĂ©ieren et net ze benotzen ouni ze realisĂ©ieren firwat Dir et braucht. DĂ«st ass nach Ă«mmer mĂ©i en Experiment wĂ©i eng sĂ©rieux "Verbesserung vun UTF-8". Trotzdem ass de Code do ordentlech, prĂ€zis geschriwwen, mat enger grousser Zuel vu Kommentaren an Testofdeckung.

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8
Test Resultater a Verglach mat UTF-8

Ech hunn och Demo SÀit, wou Dir d'Performance vum Algorithmus evaluéiere kënnt, an da wÀert ech Iech méi iwwer seng Prinzipien an Entwécklungsprozess soen.

Redundante Bits eliminéieren

Ech hunn UTF-8 als Basis geholl, natierlech. Déi éischt an déi offensichtlechst Saach, déi dran geÀnnert ka ginn, ass d'Zuel vun de Servicebits an all Byte ze reduzéieren. Zum Beispill fÀnkt den éischte Byte an UTF-8 ëmmer mat entweder un 0, oder mat 11 - e PrÀfix 10 Nëmmen déi folgend Bytes hunn et. Loosst eis de PrÀfix ersetzen 11 op 1, a fir déi nÀchst Bytes wÀerte mir d'PrÀfixe komplett ewechhuelen. Wat wÀert geschéien?

0xxxxxxx — 1 Byt
10xxxxxx xxxxxxxx - 2 Bytes
110xxxxx xxxxxxxx xxxxxxxx - 3 Bytes

Waart, wou ass de vĂ©ier-Byte Rekord? Mee et ass net mĂ©i gebraucht - wann Dir an drĂ€i Bytes schreift, hu mir elo 21 Bits zur VerfĂŒgung an dat geet duer fir all Zuelen bis zu 0x10FFFF.

Wat hu mir hei geaffert? Déi wichtegst Saach ass d'Detektioun vu Charaktergrenze vun enger arbitrÀrer Plaz am Puffer. Mir kënnen net op eng arbitrÀr Byte weisen an den Ufank vum nÀchste Charakter dovunner fannen. Dëst ass eng Begrenzung vun eisem Format, awer an der Praxis ass dëst selten néideg. Mir kënnen normalerweis vun Ufank un duerch de Puffer lafen (besonnesch wann et ëm kuerz Linnen geet).

D'Situatioun mat der Ofdeckung vu Sprooche mat 2 Bytes ass och besser ginn: elo gëtt den Zwee-Byte Format eng Rei vu 14 Bits, an dat sinn Coden bis zu 0x3FFF. D'Chinesen sinn onglécklech (hir Charaktere reechen meeschtens vun 0x4E00 ze 0x9FFF), awer Georgianer a vill aner Vëlker hu méi Spaass - hir Sprooche passen och an 2 Bytes pro Charakter.

Gitt den Encoder Staat

Loosst eis elo iwwer d'Eegeschafte vun de Linnen selwer denken. Am Wierderbuch enthÀlt meeschtens Wierder, déi a Charaktere vum selwechte Alphabet geschriwwe sinn, an dat gëllt och fir vill aner Texter. Et wier gutt dëst Alphabet eemol unzeginn, an dann nëmmen d'Zuel vum Buschtaf dran uginn. Loosst eis kucken ob d'Arrangement vun de Personnagen an der Unicode Dësch eis hëlleft.

Wéi uewen ernimmt, ass Unicode opgedeelt an Fliger 65536 Coden all. Awer dëst ass net eng ganz nëtzlech Divisioun (wéi scho gesot, meeschtens si mir am Nullfliger). Méi interessant ass d'Divisioun duerch spÀren. Dës BerÀicher hunn net méi eng fix LÀngt, a si méi sënnvoll - als Regel, kombinéiert all Zeechen aus dem selwechten Alphabet.

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8
E Block mat Charaktere vum Bengalesche Alphabet. Leider, aus historesche Grënn, ass dëst e Beispill vun net ganz dichter Verpakung - 96 Charaktere si chaotesch iwwer 128 Blockcodepunkte verspreet.

D'Ufanks vu Blocken an hir Gréissten sinn ëmmer Multiple vu 16 - dëst gëtt einfach gemaach fir d'Bequemlechkeet. Ausserdeem fÀnken a vill Blocken op WÀerter op, déi Multiple vun 128 oder souguer 256 sinn - zum Beispill, dat Basis kyrillescht Alphabet hëlt 256 Bytes aus 0x0400 ze 0x04FF. Dëst ass ganz bequem: wa mir de PrÀfix eemol spÀicheren 0x04, da kann all kyrillescht Zeechen an engem Byte geschriwwe ginn. True, sou wÀerte mir d'Méiglechkeet verléieren op ASCII zréckzekommen (an all aner Personnagen am Allgemengen). Dofir maache mir dat:

  1. Zwee Bytes 10yyyyyy yxxxxxxx net nëmmen e Symbol mat enger Zuel bezeechnen yyyyyy yxxxxxxx, awer och Ànneren aktuell Alphabet op yyyyyy y0000000 (dh mir erënneren all d'Bits ausser déi mannst bedeitend 7 bëssen);
  2. Ee Byte 0xxxxxxx dat ass de Charakter vum aktuellen Alphabet. Et muss just op d'Offset bĂ€igefĂŒĂŒgt ginn, dĂ©i mir am SchrĂ«tt 1 erĂ«nnert hunn. Och wa mir d'Alphabet net geĂ€nnert hunn, ass de Offset null, also hu mir KompatibilitĂ©it mat ASCII behalen.

Och fir Coden déi 3 Bytes erfuerderen:

  1. DrÀi Bytes 110yyyyy yxxxxxxx xxxxxxxx uginn e Symbol mat enger Zuel yyyyyy yxxxxxxx xxxxxxxx, Ànneren aktuell Alphabet op yyyyyy y0000000 00000000 (huet alles erënnert ausser déi jéngst 15 bëssen), a kontrolléiert d'Këscht wou mir elo sinn laang Modus (wann Dir d'Alphabet zréck an en Duebelbyte Ànnert, wÀerte mir dëse FÀndel zrécksetzen);
  2. Zwee Bytes 0xxxxxxx xxxxxxxx am laange Modus ass et de Charakter vum aktuellen Alphabet. Ähnlech addĂ©iere mer et mat der Offset vum SchrĂ«tt 1. Deen eenzegen Ënnerscheed ass datt mir elo zwee Bytes liesen (well mir op dĂ«se Modus gewiesselt sinn).

Kléngt gutt: Elo, wa mir Zeechen aus dem selwechte 7-Bit Unicode-Bereich musse codéieren, verbrénge mir 1 extra Byte am Ufank an am Ganzen ee Byte pro Charakter.

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8
Schafft vun enger vun de fréiere Versiounen. Et schléit schonn dacks UTF-8, awer et gëtt nach Plaz fir Verbesserung.

Wat ass mĂ©i schlĂ«mm? Als Ă©ischt hu mir eng Konditioun, nĂ€mlech aktuell Alphabet Offset an Checkbox laang Modus. DĂ«st limitĂ©iert eis weider: elo kĂ«nnen dĂ©iselwecht Zeechen a verschiddene Kontexter anescht kodĂ©iert ginn. D'Sich no Substrings, zum Beispill, muss gemaach ginn andeems Dir dĂ«st berĂŒcksichtegt, an net nĂ«mmen duerch VerglĂ€icher vu Bytes. Zweetens, soubal mir d'Alfabet geĂ€nnert hunn, ass et schlecht ginn mat der KodĂ©ierung vun ASCII Zeechen (an dĂ«st ass net nĂ«mmen dat latĂ©ngescht Alphabet, awer och Basis Punktuatioun, dorĂ«nner Plazen) - se brauchen d'Alphabet erĂ«m op 0 z'Ă€nneren, dat heescht, erĂ«m en extra Byte (an dann nach een fir op eisen Haaptpunkt zrĂ©ckzekommen).

Een Alphabet ass gutt, zwee ass besser

Loosst eis probéieren eis Bit PrÀfixe e bëssen z'Ànneren, an e méi vun den drÀi uewen beschriwwen ze pressen:

0xxxxxxx - 1 Byte am normale Modus, 2 am laange Modus
11xxxxxx — 1 Byt
100xxxxx xxxxxxxx - 2 Bytes
101xxxxx xxxxxxxx xxxxxxxx - 3 Bytes

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8

Elo an engem Zwee-Byte-Rekord gĂ«tt et ee manner verfĂŒgbare Bit - Code Punkten bis 0x1FFF, an net 0x3FFF. WĂ©i och Ă«mmer, et ass nach Ă«mmer merkbar mĂ©i grouss wĂ©i an Duebelbyte UTF-8 Coden, dĂ©i meescht ĂŒblech Sprooche passen nach Ă«mmer an, de merkbarsten Verloscht ass erausgefall hiragana Đž katakana, d'Japaner sinn traureg.

Wat ass dësen neie Code? 11xxxxxx? Dëst ass e klengen "Stash" vu 64 Zeechen an der Gréisst, et ergÀnzt eis Haaptalfabet, also hunn ech et auxiliary genannt (auxiliary) Alphabet. Wa mir dat aktuellt Alphabet wiesselen, gëtt e Stéck vum alen Alphabet Auxiliary. Zum Beispill hu mir vun ASCII op Kyrillesch gewiesselt - de Stash enthÀlt elo 64 Zeechen mat LatÀin Alphabet, Zuelen, Raum a Komma (heefegste Insertiounen an Net-ASCII Texter). Wiesselt zréck op ASCII - an den Haaptdeel vum kyrillesche Alphabet gëtt zum Hilfsalfabet.

Duerch den Zougang zu zwee Alphabete kënne mir eng grouss Unzuel vun Texter mat minimalem KÀschte fir Alphabeten ëmsetzen (Punctuatioun féiert meeschtens zu engem Retour op ASCII, awer duerno kréie mir vill net-ASCII Zeechen aus dem zousÀtzleche Alphabet, ouni erëm wiesselen).

Bonus: PrĂ€fix vum Ënner-Alfabet 11xxxxxx a wielt seng initial Offset ze sinn 0xC0, mir krĂ©ien deelweis KompatibilitĂ©it mat CP1252. An anere Wierder, vill (awer net all) westeuropĂ€esch Texter, dĂ©i am CP1252 kodĂ©iert sinn, wĂ€erten d'selwecht an UTF-C ausgesinn.

Hei entsteet awer eng Schwieregkeet: Wéi kritt een en Auxiliary aus dem Haaptalfabet? Dir kënnt dee selwechte Offset verloossen, awer - leider - hei spillt d'Unicode Struktur scho géint eis. Ganz dacks ass den Haaptdeel vum Alphabet net am Ufank vum Block (zum Beispill, déi russesch Haaptstad "A" huet de Code 0x0410, obwuel de kyrillesche Block ufÀnkt mat 0x0400). Also, nodeems mir déi éischt 64 Zeechen an d'Stash geholl hunn, kënne mir den Zougang zum Schwanzdeel vum Alphabet verléieren.

Fir dëse Problem ze fixéieren, sinn ech manuell duerch e puer Blocks duerchgaang, déi zu verschiddene Sproochen entspriechen, an hunn d'Offset vum Hilfsalfabet am Haaptalfabet fir si spezifizéiert. Dat laténgescht Alfabet, als Ausnam, gouf allgemeng nei bestallt wéi base64.

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8

Finale Touch

Loosst eis endlech iwwerdenken, wou mir soss eppes kënne verbesseren.

NotĂ©iert datt d'Format 101xxxxx xxxxxxxx xxxxxxxx erlaabt Iech Zuelen ze codĂ©ieren bis 0x1FFFFF, an Unicode endet frĂ©ier, um 0x10FFFF. An anere Wierder, wĂ€ert de leschte Code Punkt vertruede ginn als 10110000 11111111 11111111. Dofir kĂ«nne mir soen datt wann den Ă©ischte Byte vun der Form ass 1011xxxx (Wou xxxx mĂ©i wĂ©i 0), dann heescht et soss eppes. Zum Beispill kĂ«nnt Dir nach 15 Zeechen do addĂ©ieren, dĂ©i stĂ€nneg fir KodĂ©ierung an engem Byte verfĂŒgbar sinn, awer ech hunn decidĂ©iert et anescht ze maachen.

Loosst eis dĂ«s Unicode Blocks kucken, dĂ©i elo drĂ€i Bytes erfuerderen. Prinzipiell, wĂ©i scho gesot, sinn dĂ«s chinesesch Charaktere - awer et ass schwĂ©ier eppes mat hinnen ze maachen, et sinn 21 Tausend vun hinnen. Awer hiragana a katakana sinn och dohinner geflunn - an et sinn net mĂ©i sou vill, manner wĂ©i zweehonnert. A well mir d'Japaner erĂ«nnert hunn, ginn et och Emojis (tatsĂ€chlech sinn se op ville Plazen an Unicode verspreet, awer d'Haaptblocken sinn am BerĂ€ich 0x1F300 - 0x1FBFF). Wann Dir un d'Tatsaach denkt datt et elo Emojis sinn dĂ©i aus verschiddene Codepunkte glĂ€ichzĂ€iteg zesummegesat ginn (zum Beispill den Emoji ‍‍‍En anere VĂ«lo: mir spĂ€icheren Unicode Saiten 30-60% mĂ©i kompakt wĂ©i UTF-8 besteet aus esou vill wĂ©i 7 Coden!), Da gĂ«tt et komplett schued drĂ€i Bytes op all eent ze verbrĂ©ngen (7 × 3 = 21 Bytes fir d'Wuel vun enger Ikon, engem Albdram).

Dofir wielt e puer ausgewielte BerÀicher entspriechend Emoji, Hiragana a Katakana, numeréieren se an eng kontinuéierlech Lëscht a codéiere se als zwee Bytes anstatt drÀi:

1011xxxx xxxxxxxx

Super: dee sougenannten EmojiEn anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8, besteet aus 7 Code Punkten, hëlt 8 Bytes an UTF-25, a mir passen et an 14 (genau zwee Bytes fir all Code Punkt). Iwwregens, Habr refuséiert et ze verdauen (souwuel am alen wéi och am neien Editor), also ech hu missen et mat enger Foto asetzen.

Loosst eis probéieren ee méi Problem ze fixéieren. Wéi mir eis erënneren, ass d'Basis Alphabet am Wesentlechen héich 6 bëssen, déi mir am Kapp behalen an un de Code vun all nÀchst decodéiert Symbol gekollt. Am Fall vu chinesesche Charakteren déi am Block sinn 0x4E00 - 0x9FFF, dëst ass entweder Bit 0 oder 1. Dëst ass net ganz bequem: mir mussen d'Alphabet stÀnneg tëscht dësen zwee WÀerter wiesselen (dh drÀi Bytes verbréngen). Awer bemierkt datt am laange Modus, aus dem Code selwer, d'Zuel vun den Zeeche subtrahéiere kënnen, déi mir am Kuerzmodus codéieren (no all den Tricken uewen beschriwwen, dat ass 10240) - da wÀert d'Band vun den Hieroglyphen op 0x2600 - 0x77FF, an an dësem Fall, an dësem ganze BerÀich, wÀerten déi bedeitendst 6 Bits (vun 21) glÀich sinn 0. Sou wÀerte Sequenzen vun Hieroglyphen zwee Bytes pro Hieroglyph benotzen (wat fir esou eng grouss Gamme optimal ass), ouni Ursaach Alphabet Schalter.

Alternativ Léisungen: SCSU, BOCU-1

Unicode Experten, nodeems se just den Titel vum Artikel gelies hunn, wÀerte méiglecherweis séier drun erënneren datt et direkt ënner den Unicode Standarden ass Standard Kompressiounsschema fir Unicode (SCSU), déi eng Kodéierungsmethod ganz Àhnlech wéi déi am Artikel beschriwwen beschreift.

Ech ginn éierlech zou: Ech hunn iwwer seng Existenz eréischt geléiert nodeems ech déif an d'Schreiwen vun menger Entscheedung ënnerworf war. Wann ech dat vun Ufank un gewosst hÀtt, hÀtt ech wahrscheinlech probéiert eng Implementatioun ze schreiwen anstatt meng eegen Approche ze kommen.

Wat interessant ass, ass datt SCSU Iddie benotzt ganz Ă€hnlech wĂ©i dĂ©i, dĂ©i ech eleng erstallt hunn (amplaz vum Konzept vun "Alphabeten" benotze se "FĂ«nsteren", an et gi mĂ©i verfĂŒgbar wĂ©i ech hunn). Zur selwechter ZĂ€it huet dĂ«st Format och Nodeeler: et ass e bĂ«sse mĂ©i no un d'Kompressiounsalgorithmen wĂ©i d'KodĂ©ierung. Besonnesch gĂ«tt de Standard vill Representatiounsmethoden, awer seet net wĂ©i Dir dĂ©i optimal wielt - dofir muss den Encoder eng Art Heuristik benotzen. Also, e SCSU Encoder dee gutt Verpakung produzĂ©iert wĂ€ert mĂ©i komplex a mĂ©i Ă«mstĂ€ndlech sinn wĂ©i mĂ€i Algorithmus.

Zum Verglach hunn ech eng relativ einfach Implementatioun vu SCSU op JavaScript iwwerginn - wat de Codevolumen ugeet, huet et sech verglÀichbar mat menger UTF-C erausgestallt, awer an e puer FÀll war d'Resultat zéng Prozent méi schlëmm (heiansdo kann et iwwerschreiden, awer net vill). Zum Beispill, Texter op HebrÀesch a Griichesch goufen duerch UTF-C kodéiert 60% besser wéi SCSU (wahrscheinlech wéinst hire kompakten Alphabeten).

Separat wÀert ech addéieren datt nieft SCSU et och eng aner Manéier gëtt fir Unicode kompakt ze representéieren - BOCU-1, awer et zielt fir MIME Kompatibilitéit (déi ech net gebraucht hunn) an hëlt eng liicht aner Approche fir d'Kodéierung. Ech hunn seng Effektivitéit net bewÀert, awer et schéngt mir datt et onwahrscheinlech méi héich ass wéi SCSU.

Méiglech Verbesserungen

Den Algorithmus, deen ech presentĂ©iert hunn, ass net universell vum Design (dĂ«st ass mĂ©iglecherweis wou meng Ziler am meeschte vun den Ziler vum Unicode Consortium divergĂ©ieren). Ech hu scho gesot datt et haaptsĂ€chlech fir eng Aufgab entwĂ©ckelt gouf (e mĂ©isproochegt Wierderbuch an engem PrĂ€fixbaum spĂ€icheren), an e puer vu senge Funktiounen kĂ«nnen net gutt fir aner Aufgaben passen. Awer de Fakt datt et kee Standard ass kann e Plus sinn - Dir kĂ«nnt et einfach Ă€nneren fir Äre Besoinen ze passen.

Zum Beispill, op déi offensichtlech Manéier kënnt Dir vun der PrÀsenz vum Staat lassgoen, stateless Kodéierung maachen - just d'VerÀnnerlechen net aktualiséieren offs, auxOffs О is21Bit am Encoder an Decoder. An dësem Fall wÀert et net méiglech sinn effektiv Sequenzen vun Zeechen vum selwechte Alphabet ze packen, awer et gëtt eng Garantie datt dee selwechte Charakter ëmmer mat de selwechte Bytes kodéiert ass, onofhÀngeg vum Kontext.

ZousÀtzlech kënnt Dir den Encoder op eng spezifesch Sprooch personaliséieren andeems Dir de Standardzoustand Ànnert - zum Beispill, op russesch Texter fokusséieren, den Encoder an den Decoder am Ufank setzen offs = 0x0400 О auxOffs = 0. Dëst mécht besonnesch Sënn am Fall vun stateless Modus. Am Allgemengen wÀert dëst Àhnlech sinn wéi déi al Aacht-Bit Kodéierung ze benotzen, awer ouni d'FÀhigkeit ze lÀschen fir Zeeche vun all Unicode anzeginn wéi néideg.

En aneren Nodeel, dee virdru erwÀhnt gouf, ass datt am groussen Text kodéiert an UTF-C kee séiere Wee gëtt fir d'Zeechengrenz am nootste bei engem arbitrÀren Byte ze fannen. Wann Dir déi lescht, sot, 100 Bytes vum kodéierte Puffer ofgeschnidden hutt, riskéiert Dir Dreck ze kréien, mat deem Dir nÀischt maache kënnt. D'Kodéierung ass net entwéckelt fir Multi-Gigabyte Logbicher ze spÀicheren, awer allgemeng kann dëst korrigéiert ginn. Byte 0xBF dÀerf ni als éischten Byte erschéngen (awer kann den zweeten oder drëtten sinn). Dofir, wann Dir kodéiert, kënnt Dir d'Sequenz aginn 0xBF 0xBF 0xBF all, soen, 10 KB - dann, wann Dir braucht eng Grenz ze fannen, et wÀert genuch ginn der gewielter Stéck ze scannen bis eng Àhnlech Marker fonnt ass. No der leschter 0xBF ass garantéiert den Ufank vun engem Charakter. (Beim Dekodéierung muss dës Sequenz vun drÀi Bytes natierlech ignoréiert ginn.)

Zesummefaassung

Wann Dir esou wĂ€it gelies hutt, Gratulatioun! Ech hoffen Dir, wĂ©i ech, eppes Neies gelĂ©iert (oder Är ErĂ«nnerung erfrĂ«scht) iwwer d'Struktur vun Unicode.

En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8
Demo SÀit. D'Beispill vun HebrÀesch weist d'Virdeeler iwwer béid UTF-8 an SCSU.

Déi uewe beschriwwe Fuerschung sollt net als Iwwergrëff op Standarden ugesi ginn. Wéi och ëmmer, ech sinn allgemeng zefridden mat de Resultater vu menger Aarbecht, also sinn ech frou mat hinnen deelen: zum Beispill, eng minifizéiert JS Bibliothéik weit nëmmen 1710 Bytes (an huet natierlech keng OfhÀngegkeeten). Wéi ech uewen ernimmt, hir Aarbecht kann op fonnt ginn Demo SÀit (et gëtt och eng Rei vun Texter op deenen et mat UTF-8 an SCSU verglach ka ginn).

Schlussendlech wÀert ech nach eng Kéier op FÀll opmierksam maachen an deenen UTF-C benotzt gëtt net wÀert:

  • Wann Är Linnen laang genuch sinn (vun 100-200 Zeechen). An dĂ«sem Fall sollt Dir iwwer d'Benotzung vun Kompressiounsalgorithmen wĂ©i Deflate denken.
  • Wann Dir braucht ASCII Transparenz, dat heescht, et ass wichteg fir Iech datt dĂ©i kodĂ©iert Sequenzen keng ASCII Coden enthalen dĂ©i net an der ursprĂ©nglecher String waren. De Besoin fir dĂ«st kann vermeit ginn wann Dir, wann Dir mat DrĂ«tt Partei APIen interagĂ©iert (zum Beispill, mat enger Datebank schafft), Dir d'KodĂ©ierungsresultat als abstrakt Set vu Bytes passĂ©iert an net als Saiten. Soss riskĂ©iert Dir onerwaart SchwĂ€chen ze krĂ©ien.
  • Wann Dir wĂ«llt sĂ©ier Charakter Grenzen op eng arbitrĂ€r Offset ze fannen (Zum Beispill, wann en Deel vun enger Linn beschiedegt ass). DĂ«st kann gemaach ginn, awer nĂ«mmen andeems Dir d'Linn vun Ufank un scannt (oder d'Ännerung an der viregter Sektioun applizĂ©iert).
  • Wann Dir sĂ©ier Operatiounen op den Inhalt vu Strings maache musst (sortĂ©ieren, sichen no Ënnerstringen an hinnen, concatenate). DĂ«st erfuerdert Strings fir d'Ă©ischt dekodĂ©iert ze ginn, sou datt UTF-C an dĂ«se FĂ€ll mĂ©i lues ass wĂ©i UTF-8 (awer mĂ©i sĂ©ier wĂ©i Kompressiounsalgorithmen). ZĂ«nter datt dee selwechte String Ă«mmer op dĂ©iselwecht ManĂ©ier kodĂ©iert ass, ass de genaue Verglach vun der DekodĂ©ierung net erfuerderlech a kann op Byte-by-Byte Basis gemaach ginn.

Aktualiséierung: de Benotzer Tyomitch an de Kommentaren hei drënner huet eng Grafik gepost déi d'Uwendbarkeetsgrenze vun UTF-C beliicht. Et weist datt UTF-C méi effizient ass wéi en allgemeng Zweck Kompressiounsalgorithmus (eng Variatioun vu LZW) soulaang de gepackte String méi kuerz ass ~140 Zeechen (Ech bemierken awer datt de Verglach op engem Text duerchgefouert gouf; fir aner Sproochen kann d'Resultat ënnerscheeden).
En anere Vëlo: mir spÀicheren Unicode Saiten 30-60% méi kompakt wéi UTF-8

Source: will.com

Kaaft zouverlĂ€sseg Hosting fir Site mat DDoS Schutz, VPS VDS Server đŸ”„ Kaaft zouverlĂ©issegt WebsĂ€ithosting mat DDoS-Schutz, VPS VDS Server | ProHoster