Artikel ini bertujuan untuk menyatukan dan membongkar prinsip-prinsip dan mekanisme kerja pengkodean teks, secara rinci mekanisme ini untuk membongkar dan menjelaskan. Ini akan bermanfaat bagi mereka yang hanya membayangkan kira-kira apa itu penyandian teks dan bagaimana mereka bekerja, bagaimana mereka berbeda satu sama lain, mengapa kadang-kadang karakter yang tidak dapat dibaca muncul, apa prinsip penyandian yang dimiliki oleh penyandian yang berbeda.
Untuk mendapatkan pemahaman rinci tentang masalah ini, Anda harus membaca dan menyatukan lebih dari satu artikel dan menghabiskan banyak waktu untuk hal ini. Dalam materi ini, ini semua disatukan dan, secara teori, harus menghemat waktu dan analisis menurut saya ternyata cukup rinci.
Apa yang akan terjadi di bawah cut: prinsip operasi pengkodean byte tunggal (ASCII, Windows-1251, dll), prasyarat untuk penampilan Unicode, apa itu Unicode, pengkodean Unicode UTF-8, UTF-16, perbedaannya, fitur fundamental, kompatibilitas dan ketidakcocokan pengkodean yang berbeda, prinsip pengkodean karakter, analisis praktis pengkodean dan pengodean.
Masalah dengan pengkodean sekarang tentu saja telah kehilangan relevansinya, tetapi tetap saya tidak berpikir itu akan berlebihan untuk mengetahui bagaimana mereka bekerja sekarang dan bagaimana mereka bekerja sebelumnya.
Prasyarat Unicode
Saya pikir ini layak dimulai sejak saat komputerisasi belum begitu berkembang dan hanya mendapatkan momentum. Kemudian para pengembang dan standardisator tidak berpikir bahwa komputer dan Internet akan mendapatkan popularitas dan prevalensi yang begitu besar. Sebenarnya kemudian muncul kebutuhan untuk menyandikan teks. Dalam bentuk apa perlu menyimpan surat-surat di komputer, dan dia (komputer) hanya memahami satu dan nol. Jadi pengkodean ASCII satu byte dikembangkan (kemungkinan besar itu bukan pengkodean pertama, tetapi itu adalah yang paling umum dan indikatif, oleh karena itu kami akan menganggapnya sebagai referensi). Seperti apa dia? Setiap karakter dalam pengkodean ini dikodekan dengan 8 bit. Mudah untuk menghitung bahwa, berdasarkan ini, pengkodean dapat berisi 256 karakter (delapan bit, nol atau yang 2
8 = 256).
7 bit pertama (128 karakter 2
7 = 128) dalam pengkodean ini diberikan kepada karakter Latin, karakter kontrol (seperti jeda baris, tab, dll.) Dan karakter tata bahasa. Sisanya disediakan untuk bahasa nasional. Artinya, ternyata 128 karakter pertama selalu sama, dan jika Anda ingin menyandikan bahasa ibu Anda, silakan gunakan kapasitas yang tersisa. Sebenarnya, kebun binatang kode nasional besar muncul. Dan sekarang Anda sendiri dapat membayangkan, misalnya, ketika saya berada di Rusia, saya mengambil dan membuat dokumen teks, secara default dokumen itu dibuat dalam penyandian Windows-1251 (penyandian Rusia yang digunakan di Windows) dan dikirim ke seseorang, misalnya, di AS. Bahkan fakta bahwa teman bicara saya tahu bahasa Rusia tidak akan membantunya, karena ketika ia membuka dokumen saya di komputernya (dalam editor dengan penyandian default ASCII yang sama), ia tidak akan melihat huruf Rusia, tetapi krakozyabry. Untuk lebih tepatnya, tempat-tempat dalam dokumen yang saya tulis dalam bahasa Inggris akan ditampilkan tanpa masalah, karena 128 karakter pertama dari pengkodean Windows-1251 dan ASCII adalah sama, tetapi di mana saya menulis teks Rusia, jika tidak menunjukkan pengodean yang benar dalam editornya, dalam bentuk buaya.
Saya pikir masalah dengan pengkodean nasional dapat dimengerti. Sebenarnya, ada banyak pengkodean nasional ini, dan Internet telah menjadi sangat luas, dan semua orang di dalamnya ingin menulis dalam bahasa mereka sendiri dan tidak ingin bahasa mereka terlihat seperti rambut bengkok. Ada dua jalan keluar, untuk menunjukkan untuk setiap halaman pengkodean, atau untuk membuat satu tabel karakter yang umum untuk semua karakter di dunia. Opsi kedua dimenangkan, jadi kami membuat tabel karakter Unicode.
Lokakarya kecil ASCII
Ini mungkin tampak dasar, tetapi karena saya memutuskan untuk menjelaskan semuanya secara terperinci, maka ini perlu.
Berikut adalah tabel karakter ASCII:

Di sini kita memiliki 3 kolom:
- nomor karakter desimal
- nomor karakter dalam format heksadesimal
- representasi simbol itu sendiri.
Jadi, enkode string "ok" (ASCII). Karakter "o" (eng.) Memiliki posisi 111 dalam desimal dan
6F dalam heksadesimal.
01101111
terjemahkan ini ke sistem biner -
01101111
. Simbol "k" (eng.) - posisi 107 dalam desimal dan
6B dalam heksadesimal, diterjemahkan ke dalam biner -
01101011
. Total string "ok" yang dikodekan dalam ASCII akan terlihat seperti ini -
01101111 01101011
. Proses decoding akan terbalik. Kami mengambil 8 bit, menerjemahkannya menjadi penyandian 10-desimal, mendapatkan nomor karakter, melihat tabel karakter seperti apa itu.
Unicode
Dengan prasyarat untuk membuat tabel bersama untuk semua di dunia karakter, beres. Sekarang, sebenarnya, ke meja itu sendiri. Unicode - ini adalah tabel yang ada (ini bukan penyandian, tetapi tabel simbol). Ini terdiri dari 1.114.112 posisi. Sebagian besar posisi ini belum diisi dengan simbol, jadi tidak mungkin ruang ini perlu diperluas.
Total ruang ini dibagi menjadi 17 blok, masing-masing 65.536 karakter. Setiap blok berisi kelompok karakternya sendiri. Blok nol adalah yang dasar, ini berisi karakter yang paling banyak digunakan dari semua huruf modern. Di blok kedua adalah karakter bahasa yang punah. Ada dua blok yang disediakan untuk penggunaan pribadi. Sebagian besar blok belum diisi.
Total kapasitas karakter Unicode adalah dari
0 hingga
10FFFF (dalam heksadesimal).
Karakter heksadesimal ditulis dengan awalan "U +". Sebagai contoh, blok dasar pertama termasuk karakter dari U + 0000 ke U + FFFF (dari 0 hingga 65.535), dan blok ketujuh belas terakhir dari U + 100.000 ke U + 10FFFF (dari 1.048.576 ke 1.114.111).
Nah sekarang, alih-alih kebun binatang pengkodean nasional, kami memiliki tabel komprehensif di mana semua karakter yang mungkin berguna bagi kami dienkripsi. Namun ada juga kekurangannya. Jika sebelum setiap karakter dikodekan dengan satu byte, sekarang dapat dikodekan dengan jumlah byte yang berbeda. Misalnya, untuk menyandikan semua karakter alfabet bahasa Inggris, satu byte masih cukup, misalnya, karakter "o" yang sama dalam bahasa Inggris adalah unicode U + 006F, yaitu, nomor yang sama dengan ASCII adalah
6F dalam heksadesimal dan 111 dalam desimal. Tetapi untuk mengkodekan karakter "
U + 103D5 " (ini adalah nomor Persia kuno seratus) - 103D5 dalam heksadesimal dan 66.517 dalam desimal, di sini kita membutuhkan tiga byte.
Penyandian Unicode seperti UTF-8 dan UTF-16 seharusnya sudah menyelesaikan masalah ini. Selanjutnya kita akan membicarakannya.
Utf-8
UTF-8 adalah pengodean Unicode panjang variabel yang dapat digunakan untuk mewakili karakter Unicode apa pun.
Mari kita bicara lebih banyak tentang panjang variabel, apa artinya? Hal pertama yang saya katakan adalah bahwa unit struktural (atom) dari pengkodean ini adalah byte. Fakta bahwa pengkodean variabel panjang berarti bahwa satu karakter dapat dikodekan dengan jumlah unit struktural yang berbeda dari pengkodean, yaitu, dengan jumlah byte yang berbeda. Misalnya, bahasa Latin dikodekan dalam satu byte, dan Cyrillic dalam dua byte.
Sedikit menyimpang dari topik, perlu untuk menulis tentang kompatibilitas ASCII dan UTF
Fakta bahwa karakter Latin dan struktur kontrol dasar, seperti jeda baris, tab, dll. dikodekan dengan satu byte membuat pengkodean utf kompatibel dengan pengkodean ASCII. Faktanya, bahasa Latin dan struktur kontrol terletak di tempat yang sama di ASCII dan UTF, dan fakta bahwa mereka dikodekan di sana-sini dengan satu byte memastikan kompatibilitas ini.
Mari kita ambil karakter "o" dari contoh ASCII di atas. Ingat bahwa dalam tabel karakter ASCII berada di 111 posisi, dalam bentuk bit akan
01101111
. Dalam tabel Unicode, karakter ini adalah U + 006F, yang juga akan menjadi
01101111
dalam bentuk bit. Dan sekarang, karena UTF adalah pengodean panjang variabel, karakter ini akan dikodekan dalam satu byte di dalamnya. Artinya, representasi simbol ini di kedua pengkodean akan sama. Jadi untuk seluruh rentang karakter dari 0 hingga 128. Artinya, jika dokumen Anda terdiri dari teks bahasa Inggris, maka Anda tidak akan melihat perbedaannya jika Anda membukanya dalam pengkodean UTF-8 dan UTF-16 dan ASCII (misalnya, dalam UTF-16 karakter tersebut semuanya sama-sama akan dikodekan dalam dua byte, sehingga Anda tidak akan melihat perbedaannya jika editor Anda mengabaikan nol byte), dan seterusnya hingga Anda mulai bekerja dengan alfabet nasional.
Mari kita bandingkan dalam praktiknya seperti apa ungkapan "Hello World" dalam tiga penyandian berbeda: Windows-1251 (penyandian Rusia), ISO-8859-1 (penyandian bahasa Eropa Barat), UTF-8 (penyandian kode tunggal). Inti dari contoh ini adalah frasa tersebut ditulis dalam dua bahasa. Mari kita lihat bagaimana tampilannya dalam pengkodean yang berbeda.
Dalam pengkodean ISO-8859-1 tidak ada karakter seperti "m", "dan" dan "p".Sekarang mari kita bekerja dengan pengkodean dan melihat bagaimana mengkonversi string dari satu pengkodean ke yang lain dan apa yang akan terjadi jika konversi salah, atau itu tidak dapat dilakukan karena perbedaan dalam pengkodean.
Kami berasumsi bahwa frasa awalnya disandikan di Windows-1251. Berdasarkan tabel di atas, kami menulis frasa ini dalam bentuk biner, yang dikodekan dalam Windows-1251. Untuk melakukan ini, kita hanya perlu menerjemahkan simbol dari biner ke desimal atau heksadesimal (dari tabel di atas).
01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000
Nah, ini adalah frasa "Hello World" yang disandikan di Windows-1251.Sekarang bayangkan Anda memiliki file dengan teks, tetapi tidak tahu di mana penyandian teks ini. Anda menganggap bahwa itu dikodekan dalam ISO-8859-1 dan membukanya di editor Anda dalam pengkodean itu. Seperti dikatakan di atas, dengan bagian dari simbol-simbol semuanya teratur, mereka berada dalam pengkodean ini, dan bahkan berada di tempat yang sama, tetapi dengan simbol-simbol dari kata "dunia" semuanya menjadi lebih rumit. Karakter-karakter ini tidak ada dalam pengkodean ini, dan di tempat mereka dalam pengkodean ISO-8859-1 adalah karakter yang sama sekali berbeda. Secara khusus, "m" adalah posisi 236, "dan" adalah 232. "p" adalah 240. Dan pada posisi ini dalam pengkodean ISO-8859-1 adalah posisi karakter berikut 236 - karakter "ì", 232 - "è", 240 - "ð"
Jadi frasa “Hello World” yang dikodekan di Windows-1251 dan dibuka di pengkodean ISO-8859-1 akan terlihat seperti ini: “Hello ìèð”. Jadi ternyata kedua penyandian ini hanya kompatibel sebagian, dan tidak akan berfungsi dengan benar untuk menyandikan string dari satu penyandian ke penyandian lainnya, karena tidak ada karakter seperti itu.
Di sini pengkodean Unicode akan diperlukan, dan khususnya dalam kasus ini, pertimbangkan UTF-8. Fakta bahwa karakter di dalamnya dapat dikodekan dengan jumlah byte yang berbeda dari 1 hingga 4 yang telah kami temukan. Sekarang layak untuk mengatakan bahwa menggunakan UTF dapat dikodekan tidak hanya 256 karakter, seperti pada dua karakter sebelumnya, tetapi lakukan semua karakter Unicode
Ini berfungsi sebagai berikut. Bit pertama dari setiap byte karakter pengkodean tidak bertanggung jawab atas karakter itu sendiri, tetapi untuk menentukan byte. Itu, misalnya, jika bit (pertama) pertama adalah nol, maka ini berarti bahwa hanya satu byte yang digunakan untuk menyandikan karakter. Yang menyediakan kompatibilitas dengan ASCII. Jika Anda hati-hati melihat tabel karakter ASCII, Anda akan melihat bahwa 128 karakter pertama (alfabet Inggris, karakter kontrol dan tanda baca) jika mereka dikonversi ke biner, semuanya dimulai dengan bit nol (hati-hati jika Anda menerjemahkan karakter ke dalam sistem biner menggunakan misalnya online konverter, maka bit nol terkemuka pertama dapat dibuang, yang dapat membingungkan).
01001000
- bit pertama adalah nol, lalu 1 byte mengkodekan 1 karakter -> "H"
01100101
- bit pertama adalah nol, yang berarti 1 byte mengkodekan 1 karakter -> "e"
Jika bit pertama bukan nol, maka karakter dikodekan dalam beberapa byte.
Untuk karakter byte ganda, tiga bit pertama harus - 110
110 10000 10 111100
- pada awal 110, lalu 2 byte mengkodekan 1 karakter. Byte kedua dalam kasus ini selalu dimulai dengan 10. Secara total, buang bit kontrol (yang awal, yang disorot dalam warna merah dan hijau) dan ambil semua
10000111100
tersisa (
10000111100
), terjemahkan ke dalam heksadesimal (043) -> U + 043C di Unicode, simbol “m ".
untuk karakter tiga byte dalam byte pertama, bit-bit utama adalah 1110
1110 1000 10 000111 10 1010101
- kami meringkas semuanya kecuali bit kontrol dan kami mendapatkan bahwa dalam heksadesimal itu adalah 103V5, U + 103D5 adalah digit Persia kuno seratus (
10000001111010101
)
untuk karakter empat-byte dalam byte pertama, bit-bit utama adalah 11110
11110 100 10 001111 10 111111 10 111111
- U + 10FFFF adalah karakter valid terakhir dalam tabel unicode (
100001111111111111111
)
Sekarang, jika diinginkan, kami dapat merekam frasa kami dalam pengkodean UTF-8.
Utf-16
UTF-16 juga merupakan pengkodean panjang variabel. Perbedaan utamanya dari UTF-8 adalah bahwa unit struktural di dalamnya bukan hanya satu tetapi dua byte. Artinya, dalam pengkodean UTF-16, setiap karakter Unicode dapat dikodekan dengan dua atau empat byte. Demi kejelasan, izinkan saya memanggil sepasang byte tersebut sebagai pasangan kode. Berdasarkan ini, setiap karakter Unicode yang dikodekan dalam UTF-16 dapat dikodekan dengan satu atau dua pasangan kode.
Mari kita mulai dengan karakter yang dikodekan oleh satu pasangan kode. Sangat mudah untuk menghitung bahwa ada 65.535 karakter seperti itu (2v16), yang sepenuhnya bertepatan dengan blok Unicode dasar. Semua karakter yang ada di blok Unicode ini dalam pengkodean UTF-16 akan dikodekan dengan satu pasangan kode (dua byte), semuanya sederhana di sini.
simbol "o" (Latin) -
00000000 01101111
simbol "M" (Sirilik) -
00000100 00011100
Sekarang pertimbangkan karakter di luar jangkauan Unicode dasar. Untuk penyandiannya, diperlukan dua pasangan kode (4 byte). Dan mekanisme untuk mengkodekannya sedikit lebih rumit, mari kita mulai.
Untuk mulai dengan, kami memperkenalkan konsep pasangan pengganti. Pasangan pengganti adalah dua pasangan kode yang digunakan untuk menyandikan satu karakter (total 4 byte). Untuk pasangan pengganti seperti itu, rentang khusus dari
D800 ke
DFFF diberikan dalam tabel Unicode. Ini berarti bahwa ketika mengkonversi pasangan kode dari bentuk byte ke heksadesimal, Anda mendapatkan angka dari rentang ini, maka ini bukan karakter independen, tetapi pasangan pengganti.
Untuk menyandikan karakter dari rentang
10000 -
10FFFF (yaitu, karakter yang Anda perlukan untuk menggunakan lebih dari satu pasangan kode) Anda perlu:
- Kurangi 10.000 (heksadesimal) dari kode karakter (ini adalah angka terkecil dari rentang 10000 - 10FFFF )
- sebagai hasil dari poin pertama, angka yang tidak lebih besar dari FFFFF akan diperoleh, menempati hingga 20 bit
- 10 bit terdepan dari angka yang diterima dijumlahkan dengan D800 (awal kisaran pasangan pengganti dalam Unicode)
- 10 bit berikutnya dijumlahkan dengan DC00 (juga angka dari kisaran pasangan pengganti)
- setelah itu kita mendapatkan 2 pasang pengganti masing-masing 16 bit, 6 bit pertama dalam setiap pasangan bertanggung jawab untuk menentukan bahwa itu adalah pengganti,
- bit kesepuluh di setiap pengganti bertanggung jawab atas pesanannya; jika itu adalah 1, maka ini adalah pengganti pertama, jika 0, maka yang kedua
Kami akan menganalisis ini dalam praktiknya, saya pikir itu akan menjadi lebih jelas.
Misalnya, kami mengenkripsi simbol, dan mendekripsi. Ambil nomor Persia kuno seratus (U + 103D5):
- 103D5 - 10.000 = 3D5
- 3D5 =
0000000000 1111010101
(10 bit terkemuka ternyata menjadi nol, kami membawa ini ke angka heksadesimal, kami mendapatkan 0 (sepuluh pertama), 3D5 (sepuluh kedua)) - 0 + D800 = D800 (
110110 0 000000000
) 6 bit pertama menentukan bahwa angka dari kisaran pasangan memasangkan bit kesepuluh (di sebelah kanan) adalah nol, maka ini adalah pengganti pertama - 3D5 + DC00 = DFD5 (
110111 1 111010101
) 6 bit pertama menentukan bahwa angka dalam kisaran pasangan pengganti adalah bit kesepuluh (di sebelah kanan) adalah satu, maka ini adalah pengganti kedua - Total karakter ini di UTF-16 adalah
1101100000000000 1101111111010101
Sekarang decode sebaliknya. Katakanlah kita memiliki kode seperti itu - 1101100000100010 1101111010001000:
- diterjemahkan ke dalam bentuk heksadesimal = D822 DE88 (kedua nilai berasal dari kisaran pasangan pengganti, jadi kita miliki sebelum pasangan berpasangan)
110110 0 000100010
- bit kesepuluh (di sebelah kanan) adalah nol, kemudian pengganti pertama110111 1 010001000
- bit kesepuluh (di sebelah kanan) adalah satu, kemudian pengganti kedua- kita membuang 6 bit dari mereka yang bertanggung jawab untuk menentukan pengganti, kita mendapatkan
0000100010 1010001000
( 8A88 ) - tambahkan 10.000 (rentang pengganti lebih sedikit) 8A88 + 10000 = 18A88
- lihat di tabel unicode karakter U + 18A88 = Tangut Component-649. Komponen skrip Tangut.
Terima kasih kepada mereka yang mampu membaca sampai akhir, saya harap ini bermanfaat dan tidak terlalu membosankan.
Berikut ini beberapa tautan menarik tentang topik ini:
habr.com/en/post/158895 - informasi umum yang berguna tentang penyandian
habr.com/en/post/312642 - tentang Unicode
unicode-table.com/ru - tabel karakter Unicode itu sendiri
Sebenarnya di mana Anda akan tanpanya
en.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4 - Unicode
en.wikipedia.org/wiki/ASCII - ASCII
en.wikipedia.org/wiki/UTF-8 - UTF-8
en.wikipedia.org/wiki/UTF-16 - UTF-16