Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8

Upami anjeun pamekar sareng anjeun disanghareupan ku tugas milih encoding, maka Unicode ampir sok janten solusi anu pas. Metodeu ngagambarkeun spésifik gumantung kana konteks, tapi paling sering aya jawaban universal di dieu teuing - UTF-8. Hal anu hadé ngeunaan éta nyaéta ngamungkinkeun anjeun ngagunakeun sadaya karakter Unicode tanpa ngaluarkeun teuing loba bait dina kalolobaan kasus. Leres, pikeun basa anu nganggo langkung ti ngan ukur hurup Latin, "henteu seueur teuing" sahenteuna dua bait per karakter. Naha urang tiasa langkung saé tanpa uih deui ka panyandian prasejarah anu ngabatesan urang ngan ukur 256 karakter anu sayogi?

Di handap ieu kuring nyarankeun pikeun familiarize diri sareng usaha kuring pikeun ngajawab patarosan ieu sareng nerapkeun algoritma anu kawilang saderhana anu ngamungkinkeun anjeun pikeun nyimpen garis dina kalolobaan basa di dunya tanpa nambihan redundansi anu aya dina UTF-8.

Bantahan. Kuring bakal geuwat nyieun sababaraha reservations penting: solusi dijelaskeun teu ditawarkeun salaku gaganti universal pikeun UTF-8, Ieu ngan cocog dina daptar sempit kasus (nu langkung lengkep ihwal aranjeunna handap), sarta dina sagala hal teu kudu dipaké pikeun berinteraksi sareng API pihak-katilu (anu malah teu terang ngeunaan eta). Paling sering, algoritma komprési tujuan umum (contona, deflate) cocog pikeun neundeun kompak tina volume badag data téks. Salaku tambahan, parantos dina prosés nyiptakeun solusi kuring, kuring mendakan standar anu tos aya dina Unicode sorangan, anu ngarengsekeun masalah anu sami - éta rada pajeulit (sareng sering parah), tapi tetep éta standar anu katampi, sanés ngan ukur nempatkeun. babarengan dina tuur. Kuring gé ngabejaan Anjeun tentang anjeunna ogé.

Ngeunaan Unicode sareng UTF-8

Pikeun mimitian ku, sababaraha kecap ngeunaan naon éta Unicode и UTF-8.

Sakumaha anjeun terang, encodings 8-bit baheulana populer. Kalawan aranjeunna, sagalana éta basajan: 256 karakter bisa wilanganana kalawan angka ti 0 nepi ka 255, sarta angka ti 0 nepi ka 255 jelas bisa digambarkeun salaku hiji bait. Upami urang balik deui ka awal, encoding ASCII tos dugi ka 7 bit, janten bit anu paling penting dina perwakilan baitna nyaéta nol, sareng kalolobaan encoding 8-bit cocog sareng éta (anu béda ngan dina "luhureun"). bagian, dimana bit paling signifikan nyaéta hiji).

Kumaha Unicode béda sareng énkripsi éta sareng naha seueur perwakilan khusus anu aya hubunganana - UTF-8, UTF-16 (BE sareng LE), UTF-32? Hayu urang nyortir eta kaluar dina urutan.

Standar Unicode dasar ngan ngajelaskeun korespondensi antara karakter (sareng dina sababaraha kasus, komponén individu karakter) sareng nomerna. Tur aya loba kamungkinan angka dina standar ieu - ti 0x00 ka 0x10FFFF (1 lembar). Lamun urang hayang nempatkeun angka dina rentang misalna kana variabel, moal 114 atawa 112 bait bakal cukup pikeun urang. Sarta saprak prosesor urang teu pisan dirancang pikeun gawé bareng angka tilu-bait, urang bakal kapaksa ngagunakeun saloba 1 bait per karakter! Ieu UTF-2, tapi justru kusabab ieu "wastefulness" format ieu teu populér.

Untungna, urutan karakter dina Unicode henteu acak. Sakabéh set maranéhanana dibagi kana 17 "pesawat", nu masing-masing ngandung 65536 (0x10000) "titik kode" Konsep "titik kode" di dieu téh saukur angka karakter, ditugaskeun ku Unicode. Tapi, sakumaha anu kasebut di luhur, dina Unicode henteu ngan ukur karakter individu anu wilanganana, tapi ogé komponén sareng tanda jasana (sareng kadang henteu aya anu cocog sareng nomerna - sigana samentawis, tapi pikeun urang ieu henteu penting pisan), janten. eta leuwih bener sok ngobrol husus ngeunaan jumlah angka sorangan, jeung teu simbol. Nanging, di handap ieu, pikeun singgetan, kuring sering nganggo kecap "simbol", nunjukkeun istilah "titik kode".

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8
pesawat Unicode. Sakumaha anjeun tiasa tingali, lolobana eta (pesawat 4 ka 13) masih teu dipaké.

Anu paling luar biasa nyaéta yén sadaya "pulp" utama aya dina pesawat enol, éta disebut "Plane Multilingual Dasar". Upami garis ngandung téks dina salah sahiji basa modéren (kaasup Cina), anjeun moal ngalangkungan pesawat ieu. Tapi anjeun ogé moal tiasa motong sesa Unicode - contona, emoji biasana aya di tungtung. pesawat salajengna,"Suplemén Multilingual Plane"(Éta ngalegaan ti 0x10000 ka 0x1FFFF). Janten UTF-16 ngalakukeun ieu: sadaya karakter anu aya dina Plane Multilingual Dasar, dikodekeun "sakumaha aya" kalawan jumlah dua-bait pakait. Nanging, sababaraha nomer dina rentang ieu henteu nunjukkeun karakter khusus, tapi nunjukkeun yén saatos pasangan bait ieu urang kedah mertimbangkeun anu sanés - ku ngagabungkeun nilai opat bait ieu babarengan, urang nampi nomer anu nutupan. sakabeh rentang Unicode valid. Gagasan ieu disebut "pasangan pengganti" - anjeun tiasa kantos nguping aranjeunna.

Janten UTF-16 peryogi dua atanapi (dina kasus anu jarang pisan) opat bait per "titik kode". Ieu leuwih hade tinimbang ngagunakeun opat bait sadaya waktu, tapi Latin (jeung karakter ASCII séjén) lamun disandi ku cara kieu wastes satengah spasi dina nol. UTF-8 dirancang pikeun ngabenerkeun ieu: ASCII di dinya nempatan, sakumaha saméméhna, ngan hiji bait; kodeu ti 0x80 ka 0x7FF - dua bait; ti 0x800 ka 0xFFFF - tilu, jeung ti 0x10000 ka 0x10FFFF - opat. Di hiji sisi, alfabét Latin geus jadi alus: kasaluyuan jeung ASCII geus balik, sarta sebaran leuwih merata "nyebarkeun" ti 1 nepi ka 4 bait. Tapi alfabét lian ti Latin, sayangna, teu nguntungkeun sagala cara dibandingkeun UTF-16, sarta loba ayeuna merlukeun tilu bait tinimbang dua - rentang katutupan ku catetan dua-bait geus narrowed ku 32 kali, kalawan 0xFFFF ka 0x7FF, sareng sanés Cina atanapi, contona, Georgian kalebet di jerona. Sirilik sareng lima abjad sanésna - hurray - untung, 2 bait per karakter.

Naha ieu kajadian? Hayu urang tingali kumaha UTF-8 ngagambarkeun kode karakter:
Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8
Langsung pikeun ngagambarkeun angka, bit ditandaan ku simbol dipaké di dieu x. Ieu bisa ditempo yén dina catetan dua-bait aya ngan 11 bit sapertos (tina 16). Bit ngarah di dieu ngan boga fungsi bantu. Dina kasus catetan opat bait, 21 ti 32 bit dialokasikeun pikeun angka kode titik - eta bakal sigana yen tilu bait (anu masihan jumlahna aya 24 bit) bakal cukup, tapi spidol jasa dahar nepi teuing.

Ieu goréng? Henteu ogé. Di hiji sisi, lamun urang paduli pisan ngeunaan spasi, urang boga algoritma komprési nu bisa kalayan gampang ngaleungitkeun sagala éntropi tambahan sarta redundancy. Di sisi anu sanés, tujuan Unicode nyaéta nyayogikeun coding anu paling universal. Contona, urang tiasa mercayakeun hiji garis disandikeun dina UTF-8 ka kode nu saméméhna digawé ukur mibanda ASCII, sarta ulah sieun yén éta bakal ningali karakter ti rentang ASCII nu sabenerna teu aya (sanggeus kabeh, dina UTF-8 sadayana. bait dimimitian ku enol bit - ieu téh kahayang ASCII). Sareng upami urang ujug-ujug hoyong neukteuk buntut leutik tina senar anu ageung tanpa decoding ti mimiti (atanapi malikkeun bagian inpormasi saatos bagian anu ruksak), gampang pikeun urang mendakan offset dimana karakter dimimitian (éta cekap. pikeun ngalangkungan bait anu ngagaduhan awalan sakedik 10).

Naha lajeng invent hal anyar?

Dina waktu nu sarua, aya kalana kaayaan nalika algoritma komprési kawas deflate kirang lumaku, tapi anjeun hoyong ngahontal gudang kompak tina string. Pribadi, kuring mendakan masalah ieu nalika mikir ngeunaan ngawangun tangkal awalan dikomprés pikeun kamus badag kaasup kecap dina basa wenang. Di hiji sisi, unggal kecap pondok pisan, jadi compressing bakal teu epektip. Di sisi séjén, palaksanaan tangkal anu kuring dianggap dirancang ku kituna unggal bait tina string disimpen dihasilkeun vertex tangkal misah, jadi ngaminimalkeun jumlah maranéhanana éta pohara kapaké. Di perpustakaan kuring Az.js (Saperti dina pymorphy2, on mana eta dumasar) masalah sarupa bisa direngsekeun saukur - string dipak kana DAWG-kamus, disimpen di dinya CP1251 heubeul alus. Tapi, sakumaha anu gampang kahartos, ieu tiasa dianggo ngan ukur pikeun alfabét kawates - garis dina basa Cina henteu tiasa ditambah kana kamus sapertos kitu.

Kapisah, abdi hoyong catetan hiji deui nuansa pikaresepeun anu timbul nalika ngagunakeun UTF-8 dina struktur data misalna. Gambar di luhur nunjukkeun yén nalika karakter ditulis salaku dua bait, bit anu aya hubunganana sareng jumlahna henteu sakaligus, tapi dipisahkeun ku sapasang bit. 10 di tengah: 110xxxxx 10xxxxxx. Kusabab ieu, nalika 6 bit handap tina bait kadua ngabahekeun dina kode karakter (ie, transisi lumangsung. 1011111110000000), teras bait munggaran ogé robih. Tétéla yén hurup "p" dilambangkeun ku bait 0xD0 0xBF, sarta salajengna "r" geus 0xD1 0x80. Dina tangkal awalan, ieu ngakibatkeun beulah titik induk jadi dua - hiji pikeun awalan. 0xD0, sarta séjén pikeun 0xD1 (sanajan sakabéh alfabét Sirilik ngan bisa disandikeun ku bait kadua).

Naon anu kuring meunang

Nyanghareupan masalah ieu, kuring mutuskeun pikeun latihan maén game kalawan bit, sarta dina waktos anu sareng meunang saeutik hadé acquainted jeung struktur Unicode sakabéhna. Hasilna nyaéta format encoding UTF-C ("C" kanggo padet), nu méakkeun teu leuwih ti 3 bait per titik kode, sarta sering pisan ngidinan Anjeun pikeun méakkeun wungkul hiji bait tambahan pikeun sakabéh garis disandi. Ieu ngakibatkeun kanyataan yén dina loba alphabets non-ASCII encoding misalna tétéla 30-60% leuwih kompak ti UTF-8.

Kuring geus dibere conto palaksanaan encoding jeung decoding algoritma dina formulir Perpustakaan JavaScript sareng Go, Anjeun bisa make eta kalawan bébas dina kode Anjeun. Tapi kuring tetep bakal ngantebkeun yén dina rasa format ieu tetep "sapédah", sareng kuring henteu nyarankeun ngagunakeun éta. tanpa sadar naha anjeun peryogi eta. Ieu masih leuwih hiji percobaan ti serius "pamutahiran UTF-8". Tapi, kode di dinya ditulis rapih, concisely, kalawan angka nu gede ngarupakeun koméntar sarta sinyalna test.

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8
Hasil tés sareng ngabandingkeun sareng UTF-8

Kuring ogé kaca demo, dimana anjeun tiasa evaluate kinerja algoritma, lajeng abdi bakal ngabejaan Anjeun langkung seueur ngeunaan prinsip sarta prosés ngembangkeun.

Ngaleungitkeun bit kaleuleuwihan

Kuring nyandak UTF-8 salaku dasar, tangtosna. Hal kahiji jeung paling atra nu bisa dirobah dina éta pikeun ngurangan jumlah bit layanan dina unggal bait. Contona, bait munggaran di UTF-8 salawasna dimimitian ku boh 0, atawa jeung 11 - awalan 10 Ngan bait handap boga eta. Hayu urang ngaganti awalan 11 dina 1, sareng pikeun bait salajengna urang bakal ngaleungitkeun awalan lengkep. Naon anu bakal kajadian?

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

Antosan, dimana catetan opat bait? Tapi éta henteu diperyogikeun deui - nalika nyerat dina tilu bait, urang ayeuna gaduh 21 bit anu sayogi sareng ieu cekap pikeun sadaya nomer dugi ka 0x10FFFF.

Naon anu urang korbankeun di dieu? Hal pangpentingna nyaéta deteksi wates karakter ti lokasi sawenang dina panyangga. Urang teu bisa nunjuk dina bait sawenang sarta manggihan awal karakter salajengna ti dinya. Ieu mangrupikeun watesan format kami, tapi dina prakna ieu jarang diperyogikeun. Urang biasana bisa ngajalankeun ngaliwatan panyangga ti pisan awal (utamana lamun datang ka garis pondok).

Kaayaan anu nutupan basa nganggo 2 bait ogé parantos langkung saé: ayeuna format dua bait masihan sauntuyan 14 bit, sareng ieu mangrupikeun kode dugi ka 0x3FFF. Urang Cina sial (karakterna lolobana ti 0x4E00 ka 0x9FFF), tapi urang Georgia sareng seueur jalma anu langkung senang - basana ogé pas kana 2 bait per karakter.

Lebetkeun kaayaan encoder

Hayu urang ayeuna mikir ngeunaan sipat garis sorangan. Kamus paling sering ngandung kecap anu ditulis dina karakter alfabét anu sami, sareng ieu ogé leres pikeun seueur téks anu sanés. Éta hadé pikeun nunjukkeun alfabét ieu sakali, teras nunjukkeun ngan ukur nomer hurup anu aya di jerona. Hayu urang tingali naha susunan karakter dina tabel Unicode bakal ngabantosan urang.

Sakumaha didadarkeun di luhur, Unicode dibagi kana pesawat 65536 kode unggal. Tapi ieu téh lain division pohara kapaké (sakumaha geus ngomong, paling sering urang dina pesawat enol). Leuwih metot nyaéta division ku blok. Kisaran ieu henteu gaduh panjang tetep, sareng langkung bermakna - sakumaha aturan, masing-masing ngagabungkeun karakter tina alfabét anu sami.

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8
Blok anu ngandung karakter alfabét Bengali. Hanjakalna, pikeun alesan sajarah, ieu mangrupikeun conto bungkusan anu henteu padet pisan - 96 karakter sumebar sacara acak dina 128 titik kode blok.

Awal blok sareng ukuranana sok lilipetan 16 - ieu dilakukeun ngan ukur pikeun genah. Salaku tambahan, seueur blok dimimitian sareng ditungtungan dina nilai anu lilipetan 128 atanapi bahkan 256 - contona, alfabét Sirilik dasar nyandak 256 bait tina 0x0400 ka 0x04FF. Ieu rada merenah: lamun urang nyimpen awalan sakali 0x04, teras sagala karakter Sirilik bisa ditulis dina hiji bait. Leres, ku cara ieu urang bakal leungit kasempetan pikeun uih deui ka ASCII (sareng karakter sanésna sacara umum). Ku kituna urang ngalakukeun ieu:

  1. Dua bait 10yyyyyy yxxxxxxx henteu ngan ukur nunjukkeun simbol sareng angka yyyyyy yxxxxxxx, tapi ogé robah alfabét ayeuna dina yyyyyy y0000000 (nyaéta urang émut sadayana bit iwal anu paling penting 7 saeutik);
  2. Hiji bait 0xxxxxxx ieu karakter alfabét ayeuna. Ieu ngan perlu ditambahkeun kana offset nu urang inget dina hambalan 1. Bari urang teu robah abjad, offset nyaeta nol, sangkan ngajaga kasaluyuan jeung ASCII.

Kitu ogé pikeun kode anu meryogikeun 3 bait:

  1. Tilu bait 110yyyyy yxxxxxxx xxxxxxxx nuduhkeun simbol jeung angka yyyyyy yxxxxxxx xxxxxxxx, robah alfabét ayeuna dina yyyyyy y0000000 00000000 (inget sagalana iwal nu leuwih ngora 15 saeutik), sareng pariksa kotak anu kami ayeuna aya panjang modeu (lamun ngarobah aksara deui ka ganda-bait, urang bakal ngareset bandéra ieu);
  2. Dua bait 0xxxxxxx xxxxxxxx dina modeu lila éta karakter alfabét ayeuna. Nya kitu, urang tambahkeun deui jeung offset ti hambalan 1. Hijina bédana éta ayeuna urang maca dua bait (sabab urang switched kana mode ieu).

Sora alus: ayeuna bari urang kudu encode karakter ti rentang Unicode 7-bit sarua, urang méakkeun 1 bait tambahan di awal jeung total hiji bait per karakter.

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8
Gawéna tina salah sahiji vérsi saméméhna. Geus mindeng ngéléhkeun UTF-8, tapi masih aya rohangan pikeun perbaikan.

Naon anu langkung parah? Anu mimiti, urang gaduh syarat, nyaéta alfabét ayeuna offset jeung kotak centang modeu panjang. Ieu salajengna ngawatesan kami: ayeuna karakter sarua bisa disandikeun béda dina konteks béda. Pilarian pikeun substrings, contona, kudu dipigawé nyokot ieu akun, teu ngan ku ngabandingkeun bait. Bréh, pas urang robah alfabét, éta jadi goréng kalayan encoding karakter ASCII (jeung ieu mah ngan ukur aksara Latin, tapi ogé tanda baca dasar, kaasup spasi) - aranjeunna merlukeun ngarobah alfabét deui ka 0, nyaeta, deui hiji bait tambahan (lajeng hiji deui pikeun meunangkeun deui ka titik utama urang).

Hiji aksara alus, dua leuwih alus

Hayu urang coba ngarobah awalan bit urang saeutik, squeezing hiji deui ka tilu ditétélakeun di luhur:

0xxxxxxx - 1 bait dina modeu normal, 2 dina modeu panjang
11xxxxxx — 1 bait
100xxxxx xxxxxxxx - 2 bait
101xxxxx xxxxxxxx xxxxxxxx - 3 bait

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8

Ayeuna dina catetan dua-bait aya hiji bit kirang sadia - titik kode nepi ka 0x1FFFteu 0x3FFF. Sanajan kitu, éta masih noticeably leuwih badag batan dina ganda-bait kode UTF-8, paling basa umum masih pas, leungitna paling noticeable geus fallen kaluar. hiragana и katakana, urang Jepang sedih.

Naon kode anyar ieu? 11xxxxxx? Ieu mangrupikeun "simpanan" leutik tina ukuran 64 karakter, éta ngalengkepan alfabét utama urang, janten kuring nyauran éta bantu (bantu) aksara. Nalika urang ngalihkeun alfabét ayeuna, sapotong alfabét anu lami janten bantu. Salaku conto, urang ngalih ti ASCII ka Cyrillic - stash ayeuna ngandung 64 karakter anu ngandung Alfabét Latin, angka, spasi jeung koma (pangseringna sisipan dina téks non-ASCII). Pindah deui ka ASCII - sareng bagian utama alfabét Sirilik bakal janten alfabét bantu.

Hatur nuhun kana aksés ka dua alphabets, urang tiasa ngadamel angka nu gede ngarupakeun téks kalawan waragad minimal keur ngaganti alphabets (tanda baca bakal paling mindeng ngakibatkeun mulang ka ASCII, tapi sanggeus éta urang bakal meunang loba karakter non-ASCII tina alfabét tambahan, tanpa. ngalih deui).

Bonus: awalan sub-alfabét 11xxxxxx sarta milih offset awal na janten 0xC0, urang meunang kasaluyuan parsial kalawan CP1252. Dina basa sejen, loba (tapi teu kabeh) téks Éropa Kulon disandikeun dina CP1252 bakal kasampak sarua dina UTF-C.

Di dieu, kumaha oge, timbul kasusah: kumaha carana ménta hiji bantu tina alfabét utama? Anjeun tiasa ngantunkeun offset anu sami, tapi - sayangna - di dieu struktur Unicode parantos maén ngalawan kami. Sering pisan bagian utama alfabét henteu di awal blok (contona, ibukota Rusia "A" gaduh kodeu. 0x0410, sanajan blok Sirilik dimimitian ku 0x0400). Ku kituna, sanggeus nyokot 64 karakter munggaran kana stash nu, urang bisa leungit aksés ka bagian buntut tina alfabét.

Pikeun ngalereskeun masalah ieu, kuring sacara manual ngalangkungan sababaraha blok anu cocog sareng basa anu béda, sareng netepkeun offset alfabét bantu dina anu utama pikeun aranjeunna. Alfabét Latin, salaku pangecualian, umumna disusun ulang sapertos base64.

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8

némpél pamungkas

Hayu urang tungtungna mikir ngeunaan mana sejenna bisa ningkatkeun hiji hal.

Catet yén format 101xxxxx xxxxxxxx xxxxxxxx ngidinan Anjeun pikeun encode angka nepi ka 0x1FFFFF, jeung Unicode ends saméméhna, di 0x10FFFF. Dina basa sejen, titik kode panungtungan bakal digambarkeun salaku 10110000 11111111 11111111. Ku alatan éta, urang bisa disebutkeun yen lamun bait kahiji mangrupa formulir 1011xxxx (Dimana xxxx leuwih gede ti 0), mangka hartina hal sejenna. Salaku conto, anjeun tiasa nambihan 15 karakter sanés anu terus-terusan sayogi pikeun encoding dina hiji bait, tapi kuring mutuskeun pikeun ngalakukeunana béda.

Hayu urang tingali blok Unicode anu peryogi tilu bait ayeuna. Dasarna, sakumaha geus disebutkeun, ieu karakter Cina - tapi hese ngalakukeun nanaon jeung aranjeunna, aya 21 sarébu di antarana. Tapi hiragana sareng katakana ogé ngapung ka dinya - sareng henteu seueur deui, kirang ti dua ratus. Sareng, saprak urang émut Jepang, aya ogé emojis (saleresna, aranjeunna sumebar di seueur tempat di Unicode, tapi blok utama aya dina kisaran. 0x1F300 - 0x1FBFF). Upami anjeun mikirkeun kanyataan yén ayeuna aya emojis anu dirakit tina sababaraha titik kode sakaligus (contona, emoji ‍‍‍Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8 diwangun ku saloba 7 Konci!), Lajeng janten éra lengkep méakkeun tilu bait dina unggal (7× 3 = 21 bait demi hiji ikon, ngimpina a).

Ku alatan éta, urang milih sababaraha rentang nu dipilih luyu jeung emoji, hiragana jeung katakana, renomer kana hiji daptar terus-terusan sarta encode jadi dua bait tinimbang tilu:

1011xxxx xxxxxxxx

Hébat: emoji anu disebut tadiSapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8, diwangun ku 7 titik kode, nyokot 8 bait di UTF-25, sarta kami cocog kana 14 (persis dua bait pikeun tiap titik kode). Ku jalan kitu, Habr nampik pikeun nyerna eta (boh dina heubeul boh dina redaktur anyar), jadi kuring kudu nyelapkeun eta kalawan gambar.

Hayu urang coba ngalereskeun hiji deui masalah. Salaku urang apal, alfabét dasar dasarna luhur 6 bit, nu urang tetep dina pikiran jeung lem kana kode unggal simbol decoded salajengna. Dina kasus karakter Cina anu di blok 0x4E00 - 0x9FFF, Ieu boh bit 0 atawa 1. Ieu teu pisan merenah: urang kudu terus-terusan pindah alfabét antara dua nilai ieu (ie méakkeun tilu bait). Tapi perhatikeun yén dina modeu panjang, tina kode sorangan urang tiasa ngirangan jumlah karakter anu urang encode nganggo mode pondok (sanggeus sadaya trik anu dijelaskeun di luhur, ieu 10240) - teras kisaran hiéroglif bakal ngalih ka 0x2600 - 0x77FF, sareng dina hal ieu, sapanjang rentang ieu, 6 bit anu paling penting (tina 21) bakal sami sareng 0. Ku kituna, sekuen hiéroglif bakal ngagunakeun dua bait per hiéroglif (anu optimal pikeun rentang anu ageung), tanpa ngabalukarkeun switch aksara.

solusi alternatif: SCSU, BOCU-1

Ahli Unicode, nembé maca judul tulisan, sigana bakal gancang-gancang ngingetkeun yén anjeun langsung diantara standar Unicode aya. Skéma komprési standar pikeun Unicode (SCSU), anu ngajelaskeun metode panyandian anu sami sareng anu dijelaskeun dina tulisan.

Kuring ngaku jujur: Kuring diajar ngeunaan ayana ngan sanggeus kuring ieu deeply immersed dina nulis kaputusan kuring. Kungsi kuring terang ngeunaan éta ti mimiti, kuring sigana bakal nyobian nyerat palaksanaan tibatan pendekatan kuring sorangan.

Anu pikaresepeun nyaéta yén SCSU ngagunakeun ideu anu sami sareng anu kuring datangkeun nyalira (tinimbang konsép "alfabét" aranjeunna nganggo "jandela", sareng aya seueur deui anu sayogi ti kuring). Dina waktos anu sami, format ieu ogé ngagaduhan kalemahan: éta rada caket kana algoritma komprési tibatan anu encoding. Khususna, standar masihan seueur padika ngagambarkeun, tapi henteu nyarios kumaha milih anu optimal - pikeun ieu, encoder kedah nganggo sababaraha jinis heuristik. Ku kituna, encoder SCSU anu ngahasilkeun bungkusan anu saé bakal langkung rumit sareng langkung pajeulit tibatan algoritma kuring.

Pikeun babandingan, kuring nransper palaksanaan kawilang basajan tina SCSU ka JavaScript - dina jihat volume kode tétéla comparable jeung UTF-C kuring, tapi dina sababaraha kasus hasilna éta puluhan persen goréng (kadangkala bisa ngaleuwihan eta, tapi). teu loba). Contona, naskah dina basa Ibrani jeung Yunani disandi ku UTF-C 60% leuwih hade tinimbang SCSU (sigana alatan hurup kompak maranéhanana).

Kapisah, kuring bakal nambahan yén salian SCSU aya ogé cara séjén pikeun kompak ngagambarkeun Unicode - BOCU-1, tapi tujuanana pikeun kasaluyuan MIME (anu kuring henteu peryogi) sareng nyandak pendekatan anu rada béda pikeun encoding. Kuring geus teu ditaksir efektivitas na, tapi sigana kuring nu masih aya kacangcayaan éta leuwih luhur ti SCSU.

Perbaikan kamungkinan

Algoritma anu kuring dibere teu universal ku desain (ieu meureun dimana tujuan kuring divergen paling ti tujuan Unicode Consortium). Kuring geus disebutkeun yen eta dikembangkeun utamana pikeun hiji tugas (nyimpen kamus multibasa dina tangkal awalan), sarta sababaraha fitur na bisa jadi teu cocog pikeun tugas séjén. Tapi kanyataan yén éta sanés standar tiasa janten tambihan - anjeun bisa kalayan gampang ngaropea eta pikeun nyocogkeun ka kabutuhan Anjeun.

Contona, dina cara atra anjeun bisa leupas tina ayana kaayaan, nyieun stateless coding - ngan teu update variabel. offs, auxOffs и is21Bit dina encoder jeung decoder. Dina hal ieu, moal mungkin mun éféktif pak runtuyan karakter tina alfabét sarua, tapi bakal aya jaminan yén karakter sarua salawasna disandikeun ku bait sarua, paduli konteks.

Salaku tambahan, anjeun tiasa nyaluyukeun encoder kana basa khusus ku cara ngarobih kaayaan standar - contona, fokus kana téks Rusia, nyetél encoder sareng decoder di awal. offs = 0x0400 и auxOffs = 0. Ieu hususna masuk akal dina kasus mode stateless. Sacara umum, ieu bakal sami sareng nganggo encoding dalapan bit lami, tapi tanpa ngaleungitkeun kamampuan nyelapkeun karakter tina sadaya Unicode upami diperyogikeun.

Kakurangan sanésna anu disebatkeun sateuacana nyaéta dina téks ageung anu disandikeun dina UTF-C teu aya jalan gancang pikeun mendakan wates karakter anu paling caket sareng bait anu sawenang. Lamun neukteuk off panungtungan, nyebutkeun, 100 bait ti panyangga disandikeun, Anjeun risiko meunang sampah nu teu bisa ngalakukeun nanaon jeung. Encoding henteu dirancang pikeun nyimpen log multi-gigabyte, tapi sacara umum ieu tiasa dilereskeun. Byte 0xBF kudu pernah muncul salaku bait kahiji (tapi bisa jadi nu kadua atawa katilu). Ku alatan éta, nalika encoding, anjeun tiasa nyelapkeun runtuyan 0xBF 0xBF 0xBF unggal, sebutkeun, 10 KB - lajeng, lamun kudu manggihan wates, éta bakal cukup pikeun nyeken sapotong dipilih nepi ka kapanggih spidol sarupa. Nuturkeun panungtungan 0xBF dijamin janten awal karakter. (Nalika decoding, sekuen tilu bait ieu, tangtosna, kedah dipaliré.)

summing up

Upami anjeun parantos maca dugi ka ieu, ucapan salamet! Kuring miharep anjeun, kawas kuring, diajar hal anyar (atawa refreshed memori anjeun) ngeunaan struktur Unicode.

Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8
Kaca demo. Conto basa Ibrani nunjukkeun kaunggulan pikeun UTF-8 sareng SCSU.

Panaliti anu dijelaskeun di luhur henteu kedah dianggap ngalanggar standar. Najan kitu, kuring umumna wareg jeung hasil karya kuring, jadi kuring senang jeung aranjeunna ngabagikeun: Contona, hiji perpustakaan JS minified beuratna wungkul 1710 bait (jeung teu boga katergantungan, tangtu). Salaku kuring disebutkeun di luhur, karya nya bisa kapanggih dina kaca demo (Aya ogé sakumpulan téks anu tiasa dibandingkeun sareng UTF-8 sareng SCSU).

Tungtungna, kuring bakal sakali deui narik perhatian kana kasus dimana UTF-C dianggo teu patut eta:

  • Upami garis anjeun cukup panjang (tina 100-200 karakter). Dina hal ieu, anjeun kedah mikir ngeunaan ngagunakeun algoritma komprési sapertos deflate.
  • Upami anjeun peryogi Transparansi ASCII, nyaeta, hal anu penting pikeun anjeun yén runtuyan disandikeun teu ngandung kode ASCII nu teu aya dina string aslina. Kabutuhan pikeun ieu tiasa dihindari upami, nalika berinteraksi sareng API pihak katilu (contona, damel sareng database), anjeun lulus hasil encoding salaku set abstrak bait, sareng sanés salaku string. Upami teu kitu, anjeun risiko meunang vulnerabilities teu kaduga.
  • Lamun hayang bisa gancang manggihan wates karakter dina hiji offset sawenang (Contona, lamun bagian tina garis ruksak). Ieu tiasa dilakukeun, tapi ngan ukur ku nyeken garis ti mimiti (atanapi nerapkeun modifikasi anu dijelaskeun dina bagian sateuacana).
  • Upami anjeun kedah gancang ngalakukeun operasi dina eusi senar (nyortir aranjeunna, milarian substrings di antarana, concatenate). Ieu merlukeun string kudu decoded munggaran, jadi UTF-C bakal leuwih laun ti UTF-8 dina kasus ieu (tapi leuwih gancang ti algoritma komprési). Kusabab string anu sami sok disandikeun ku cara anu sami, perbandingan pasti tina decoding henteu diperyogikeun sareng tiasa dilakukeun dumasar kana bait-demi-bait.

update: pamaké éta Tyomitch dina komentar di handap dipasang grafik panyorot wates panerapan UTF-C. Éta nunjukkeun yén UTF-C langkung éfisién tibatan algoritma komprési tujuan umum (variasi LZW) salami senar anu dibungkus langkung pondok. ~ 140 karakter (Nanging, kuring perhatikeun yén perbandingan dilaksanakeun dina hiji téks; pikeun basa sanés hasilna tiasa bénten).
Sapédah anu sanés: kami nyimpen senar Unicode 30-60% langkung kompak tibatan UTF-8

sumber: www.habr.com

Tambahkeun komentar