34 poin oleh GN⁺ 2025-09-13 | 1 komentar | Bagikan ke WhatsApp
  • UTF-8 adalah metode encoding dengan panjang variabel yang mampu merepresentasikan jutaan karakter sambil tetap mempertahankan kompatibilitas mundur dengan ASCII
  • Area 7-bit yang sama dengan ASCII (U+0000~U+007F) menggunakan 1 byte apa adanya, sehingga file ASCII otomatis menjadi file UTF-8 yang valid
  • Karakter lainnya direpresentasikan sebagai urutan 2~4 byte, dengan pola bit pada byte pertama menentukan panjangnya dan byte-byte berikutnya diawali 10 untuk menandakan bahwa itu adalah byte lanjutan
  • Berkat desain ini, UTF-8 dapat menangani himpunan karakter universal sekaligus tetap sepenuhnya kompatibel dengan sistem ASCII yang sudah ada, sehingga menjadi encoding karakter yang paling luas digunakan
  • Encoding Unicode lain seperti UTF-16 dan UTF-32 tidak menyediakan kompatibilitas ASCII seperti ini

Keunggulan desain UTF-8

  • Saat pertama kali mengenal encoding UTF-8, saya sangat terkesan oleh bagaimana ia dapat mencakup jutaan karakter dari berbagai bahasa dan simbol dalam satu sistem, sambil tetap memiliki struktur yang kompatibel dengan ASCII lama
  • Pada dasarnya UTF-8 memanfaatkan hingga 32 bit, tetapi ASCII hanya menggunakan 7 bit
  • Prinsip desain UTF-8 adalah sebagai berikut
    • Semua file yang diencoding dengan ASCII adalah file UTF-8 yang valid
    • Semua file UTF-8 yang hanya berisi karakter ASCII adalah file ASCII yang valid
  • Gagasan untuk menggabungkan sistem lama yang terbatas pada 128 karakter dengan sistem yang mencakup jutaan karakter benar-benar sangat inovatif

Konsep dasar UTF-8

  • UTF-8 adalah encoding karakter dengan panjang variabel (variable-width encoding) yang dirancang untuk merepresentasikan semua karakter dalam himpunan karakter Unicode
  • Setiap karakter diencoding dalam 1~4 byte
  • 128 karakter pertama (U+0000~U+007F) disimpan sebagai satu byte, sehingga kompatibilitas mundur dengan ASCII terjaga
  • Karakter lainnya diencoding dalam dua, tiga, atau empat byte
  • Bit awalan pada byte pertama menentukan jumlah total byte yang diperlukan untuk encoding tersebut
Pola 1 byte Jumlah byte Pola seluruh urutan byte
0xxxxxxx 1 0xxxxxxx (ASCII umum)
110xxxxx 2 110xxxxx 10xxxxxx
1110xxxx 3 1110xxxx 10xxxxxx 10xxxxxx
11110xxx 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  • Byte ke-2, ke-3, dan ke-4 dalam urutan multibyte selalu dimulai dengan 10, yang menandai dengan jelas bahwa itu adalah byte lanjutan
  • Sisa bit dari byte utama dan byte lanjutan digabungkan untuk membentuk satu code point
    • Code point adalah pengenal unik untuk karakter Unicode, ditulis dengan awalan "U+" dan angka heksadesimal
    • Contoh: code point untuk "A" adalah U+0041
  • Alur menafsirkan karakter dari byte encoding UTF-8 adalah sebagai berikut
    • 1. Baca satu byte; jika diawali 0, anggap itu sebagai karakter satu byte (ASCII), gunakan 7 bit sisanya untuk menampilkan karakter, lalu lanjut ke byte berikutnya
    • 2. Jika tidak diawali 0, maka
      • jika 110, berarti karakter 2 byte dan satu byte berikutnya perlu dibaca
      • jika 1110, berarti karakter 3 byte dan dua byte berikutnya perlu dibaca
      • jika 11110, berarti karakter 4 byte dan tiga byte berikutnya perlu dibaca
    • 3. Dari byte-byte yang telah ditentukan, gabungkan bit-bit selain bit awalan untuk digunakan sebagai nilai biner code point
    • 4. Cari code point tersebut dalam himpunan karakter Unicode lalu tampilkan di layar
    • 5. Ulangi untuk byte berikutnya

Contoh: karakter Hindi "अ"

  • Representasi UTF-8: 11100000 10100100 10000101 (3 byte)
  • Byte pertama (11100000) → menunjukkan bahwa ini adalah karakter 3 byte
  • Gabungan bit valid dari tiga byte → 00001001 00000101 = heksadesimal 0x0905
  • Code point U+0905 berarti karakter Devanagari "अ"

Contoh file

  • 1. Hey👋 Buddy

    • Total terdiri dari 13 byte
      • Karakter ASCII (H, e, y, B, u, d, d, y, spasi) → masing-masing 1 byte
      • 👋 (U+1F44B) → 4 byte 11110000 10011111 10010001 10001011
    • File ini adalah file UTF-8 yang valid, tetapi karena mengandung karakter non-ASCII (emoji), file ini tidak kompatibel sebagai ASCII
  • 2. Hey Buddy

    • Total 9 byte, semuanya berada dalam rentang ASCII
    • Karena itu, file ini sekaligus merupakan file ASCII yang valid dan file UTF-8 yang valid

Perbandingan dengan encoding lain

  • Ada beberapa encoding yang menyediakan kompatibilitas dengan ASCII, tetapi tidak digunakan seluas UTF-8
  • GB18030 (standar Tiongkok) dan lainnya juga menyediakan kompatibilitas ASCII, tetapi tidak banyak digunakan
  • Keluarga ISO/IEC 8859 merupakan ekstensi satu byte (maksimal 256 karakter), sehingga memiliki keterbatasan
  • UTF-16/UTF-32 tidak memiliki kompatibilitas ASCII
    • 'A' (U+0041): dalam UTF-16 adalah 00 41, dalam UTF-32 adalah 00 00 00 41

Bonus: Playground UTF-8

1 komentar

 
GN⁺ 2025-09-13
Opini Hacker News
  • Dalam UTF-8, byte lanjutan selalu diawali dengan 10, jadi meskipun melompat ke byte sembarang, kita bisa langsung mengecek apakah posisi itu adalah awal karakter atau byte lanjutan, sehingga mudah menemukan titik awal karakter berikutnya atau sebelumnya. Jika memakai encoding seperti skema integer panjang variabel EBML (pembalikan 1/0 untuk mempertahankan kompatibilitas ASCII satu byte), akan sulit mengetahui awal karakter langsung dari posisi acak. Lihat detailnya di RFC8794 section 4.4

    • Benar, itu keunggulan besar UTF-8. String UTF-8 bisa dinavigasi bebas maju-mundur tanpa harus dibaca dari awal. Dalam Python, agar indeks string bisa bekerja per karakter, CPython memakai wide characters. Dulu bisa memilih karakter 2 byte atau 4 byte, lalu kemudian beralih otomatis saat runtime. Tapi itu tetap wide character, bukan UTF-8. Misalnya, satu emoji saja bisa membuat ukuran string menjadi empat kali lipat. Saya justru pernah memikirkan pendekatan memakai UTF-8 secara internal, lalu membuat tipe indeks menjadi objek opak, sehingga jika ditambah atau dikurangi bilangan bulat kecil, ia bergerak maju-mundur di dalam string. Saat benar-benar dikonversi ke integer atau dipakai untuk subscript langsung, barulah indeks string dihitung. Dengan pendekatan seperti ini, regex dan sebagainya juga bisa memanfaatkan objek indeks opak agar bekerja baik di representasi UTF-8

    • Saya rasa LEB128/VLQ lebih baik daripada skema integer panjang variabel EBML. Pembedanya memakai MSB di dalam byte - 0 berarti akhir sekuens dan byte berikutnya memulai sekuens baru, 1 berarti mundur sampai menemukan MSB 0. Ada juga implementasi efisien yang dioptimalkan SIMD. Perbedaan LEB128 dan VLQ hanya endianness. ASCII menjadi 0xxxxxxx, karakter ekstensi menjadi 1xxxxxxx 0xxxxxxx, 1xxxxxxx 1xxxxxxx 0xxxxxxx, dan seterusnya, sehingga dalam 3 byte bisa meng-encode hingga 0x1FFFFF, lebih dari cukup untuk Unicode. Ini tidak self-synchronizing, tetapi lebih padat. ASCII tetap 1 byte, dan simbol matematika atau bahasa Jepang seperti code point di bawah U+3FFF bisa direpresentasikan dalam 2 byte, sehingga menguntungkan untuk mengurangi ukuran kode

    • Saya rasa itu hanya berlaku jika teksnya tidak rusak atau tidak dimanipulasi secara jahat. Saat mem-parsing atau meng-escape sekuens UTF-8 yang salah, sudah banyak kerentanan keamanan yang muncul. Contohnya bisa dilihat di masalah PostgreSQL CVE-2025-1094, dan juga di daftar CVE terkait UTF-8

    • Itu tidak selalu benar. Saat UTF-8 tidak valid, karakter bisa berubah menjadi byte lanjutan (continuation byte). Misalnya jika masuk sebagai 0b01100001 0b10000000 0b01100001, hasilnya menjadi tiga karakter a�a. Untuk menentukan apakah karakter output dimulai di titik itu, kita harus melihat 1-3 byte sebelumnya

    • Jika ukuran multibyte maksimum adalah 4 byte, maka cukup melihat mundur paling banyak 3 byte untuk menentukan apakah posisi saat ini adalah byte lanjutan. Jika tidak ditemukan byte awal, berarti itu karakter satu byte. Saya menduga ini dirancang untuk tujuan pemulihan, agar meski library tidak mengenali UTF-8 dengan benar, ia tetap bisa mengabaikan byte tidak valid di awal dan akhir slice yang terpotong, lalu mengekstrak string yang masih cukup masuk akal

  • Saya rasa UTF-8 benar-benar luar biasa. Intinya ada pada keputusan ASCII yang hanya memakai 7 bit. Bahkan pada 1963, pilihan 7 bit itu agak tidak biasa. Saya penasaran apakah ini murni kebetulan sejarah, apakah para perancang ASCII sempat mempertimbangkan memakai satu bit tambahan untuk simbol ekstra, atau memang sudah memikirkan code page dan extensibility

    • Saya tidak tahu alasan pastinya, tetapi dulu 8 bit tidak selalu tersedia begitu saja. Skema 7 bit + 1 parity atau bit penanda itu umum (itulah sebabnya e-mail sampai sekarang masih memakai quoted-printable untuk meng-encode 8 bit hanya dengan 7 bit). Jika 8 bit bisa diteruskan apa adanya, itu disebut 8-bit clean. Dalam konteks itu, UTF-8 pada akhirnya adalah contoh pemanfaatan bit ke-8 yang tersisa dari ASCII dengan sangat baik. Sebagai referensi, ada juga penjelasan tentang 8-bit clean

    • Saya bukan ahli, tapi dulu pernah membaca sejarah ASCII. ASCII berakar dari kode teletype (yang berkembang dari kode telegraf). Kode Morse panjangnya variabel sehingga merepotkan untuk diimplementasikan di mesin. Karena itu muncullah kode Baudot 5 bit. Itu kode panjang tetap untuk menyederhanakan mesin, sekaligus mengurangi kelelahan operator. Dari kode Baudot itulah istilah baud untuk symbol rate masih dipakai sampai sekarang. Setelah itu, dengan metode input pita berlubang menggunakan mesin ketik, fleksibilitas meningkat dan simbol khusus seperti Carriage Return dan Line Feed ditambahkan. Industri komputer awal mengadopsi punch card sebagai input, lalu IBM mengembangkan sistem 8 bit baru agar kartu bisa diproses lebih cepat, dan itulah yang menjadi dasar ASCII. Pada akhirnya, kode biner terus diperluas seiring kemajuan teknologi. ASCII sendiri adalah produk masa transisi yang muncul sebelum konvensi byte 8 bit mapan

    • Sebenarnya bit yang tersisa itu memang dipakai ulang untuk parity

    • Ekstensi 8 bit ASCII (keluarga ISO 8859-x) dipakai luas selama puluhan tahun dan masih digunakan di code page standar Windows. Bahkan kalau ASCII sejak awal 8 bit, saya rasa karakter penting tetap akan terkonsentrasi di 128 pertama, jadi tetap cocok untuk UTF-8. Kalau mau dibilang kebetulan sejarah, itu bukan karena ASCII 7 bit, melainkan karena perkembangan komputer saat itu terutama terjadi di dunia berbahasa Inggris, dan bahasa Inggris memang cukup direpresentasikan dengan 7 bit

    • 7 bit sendiri tidak terlalu aneh. Baudot 5 bit, lalu karena itu tidak cukup muncul kode 6 bit, dan setelahnya lahir ASCII 7 bit. IBM memang menstandarkan byte 8 bit (kode EBCDIC) di System/360, tetapi vendor komputer lain saat itu belum punya panjang byte yang tetap. Jadi meski 7 bit tampak aneh, saat itu karakter dan system word memang belum harus tersusun rapi satu sama lain

  • Saya setuju UTF-8 dirancang lebih baik dari yang diharapkan. Tapi Unicode punya masalah cakupan (scppe) yang terlalu luas. Muncul pertanyaan apa yang seharusnya masuk ke Unicode. Secara intuitif, orang mungkin mengira itu berarti "semua karakter cetak yang berbeda yang dipakai manusia untuk berkomunikasi", tetapi kenyataannya tidak begitu.

    • Batasnya tidak jelas. Ada code point yang memang ada untuk penggabungan (combining)

    • Tidak spesifik. Satu karakter bisa ditulis dengan beberapa cara. Karakter yang tampak sama pun bisa punya code point dan makna yang berbeda

    • Tidak semuanya printable. Ada control char. Memang dimasukkan demi kompatibilitas ASCII, tetapi control char khusus Unicode sendiri juga terus bertambah Sepertinya belum ada Unicode point yang beranimasi. Setidaknya, yang printable masih bisa dicetak di kertas. Tapi saya tidak tahu apakah sifat tetap ini akan bertahan di masa depan. Omong-omong, di antara encoding utf ada juga utf-7 yang tidak disebut penulis. Itu mirip utf-8, tetapi dibuat dengan asumsi bahwa pada lingkungan jaringan era 80-an, penggunaan bit terakhir tidak aman. Saya pernah kebetulan menerima email yang di-encode sebagai utf-7. Sampai sekarang saya masih tidak tahu bagaimana itu bisa terkirim

    • UTF-7 terutama dibuat untuk lingkungan transmisi yang tidak 8-bit clean, seperti email. Sekarang itu sudah ketinggalan zaman, dan juga tidak bisa meng-encode supplementary plane (hanya bisa lewat UTF-16 surrogate pair). Ada juga UTF-9, tapi itu parodi yang diperkenalkan di RFC edisi April Mop (untuk lingkungan 36 bit seperti PDP-10)

  • Ada hal yang selalu saya penasaran: code point Unicode bisa di-encode sebagai sekuens byte yang terlalu panjang padahal tidak perlu. UTF-8 melarang itu dan hanya mengizinkan sekuens terpendek. Misalnya 00000001 bisa juga ditulis sebagai 11000000 10000001. Kalau begitu, bukankah bisa dibuat skema lain agar encoding ilegal sama sekali tidak mungkin? Misalnya, kalau awal sekuens 2 byte dijadikan nilai valid terakhir, maka 11000000 10000001 menjadi 128+1, dan 0-127 ditangani sebagai 1 byte. Dengan begitu tidak ada kode ilegal, dan string pada edge case juga akan sedikit lebih pendek. Saya jadi penasaran apakah ini dulu tidak dipertimbangkan karena biaya hardware saat itu. (Pembaruan: sekuens bit yang benar seharusnya 10000001, sudah diperbaiki)

    • Banyak jawaban menyinggung synchronization marker, tetapi pertanyaan intinya adalah kenapa U+0080 menjadi c2 80, bukan c0 80 (nilai pertama setelah 7f). Menurut saya alasannya sebagai berikut a) Jika overlong encoding diizinkan, sebagian implementasi yang hanya memeriksa sekuens pendek akan memiliki celah keamanan b) Encoding/decoding UTF-8 standar bisa diproses hanya dengan masking (bitmask) dan shifting (bitshift). Skema yang Anda usulkan membutuhkan operasi pengurangan tambahan Hal ini pernah dibahas dalam diskusi email tahun 1992, dan FSS-UTF memang memakai additive constants (lihat di bawah)

    Sekuens 2 byte dapat memuat 2^11 kode, tetapi 0-7f ilegal. Tampaknya ini dianggap lebih baik daripada additive constants yang tidak memberi kompensasi khusus
    Lihat detailnya di bagian paling bawah utf-8-history.txt

    • Menjaga self-synchronicity pola byte adalah kuncinya. Jika byte lanjutan tidak dipertahankan seperti pada 11000000 10000001, kita akan kehilangan kemampuan untuk selalu menemukan batas code point dalam stream UTF-8 yang terpotong. Jika ditambah operasi tambah/kurang seperti itu, performa decoder juga akan turun. Sekarang semuanya bisa diproses hanya dengan operasi bit

    • Seperti komentar quectophoton, byte lanjutan harus selalu diawali dengan 10 agar parser bisa menemukan batas code point dari titik mana pun. Ini memang hasil pertimbangan saat UTF-8 dirancang di awal 90-an, ketika lingkungan transmisi yang tidak andal masih sangat umum

    • Dengan skema yang diusulkan, perhitungan encoding/decoding jadi lebih rumit dan lebih lambat. Sekarang cukup beberapa bit shift saja, tetapi pada masa itu (era 90-an) hal itu penting karena komputer masih lambat

  • Jika ingin membaca lebih jauh tentang desain UTF-8, lihat one-pager dari Russ Cox dan ringkasan sejarah dari Rob Pike

  • UTF-8 itu hebat dan akan sangat bagus jika dipakai di semua lingkungan (saya sedang melihatmu, JavaScript). Tetapi satu-satunya kelemahan menurut saya adalah standar tidak menjelaskan dengan jelas cara menafsirkan sekuens byte yang tidak valid. Akan lebih sempurna jika desainnya "selalu menetapkan cara interpretasi untuk setiap sekuens byte". Menurut saya, pendekatan seperti spesifikasi HTML5 bisa berhasil dijalankan

    • Dari sisi keamanan, UTF-8 yang salah seharusnya jangan ditangani sama sekali; datanya langsung dibuang dan diperlakukan seperti bahan berbahaya, lalu kembalikan error. Kalau tidak, Anda akan terbuka terhadap serangan yang mem-bypass validasi
  • Saya punya hubungan cinta-benci dengan kompatibilitas mundur (backwards compatibility). Saya tidak suka hal-hal yang membingungkan, tetapi saya juga cenderung menyukai dorongan untuk maju meskipun harus merusak sesuatu. Namun di saat yang sama, saya senang melihat contoh seperti UTF-8 atau EAN yang tetap kompatibel tetapi dirancang dengan cerdas. Jujur saja, rasanya UTF-8 hampir tidak mengorbankan apa pun demi kompatibilitas

    • rasanya UTF-8 hampir tidak mengorbankan apa pun demi kompatibilitas
      Encoding di atas 21 bit diblokir. Ini karena kompatibilitas UTF-16 (mekanisme surrogate di UTF-16 hanya bisa mencapai 2^21-1). Bisa saja suatu hari kita menyesali batas ini. Sepertinya tidak ada alasan praktis lain untuk melarang code point di atas 21 bit selain itu

    • saya suka ketika orang yang berkuasa berani mengubah sesuatu atas nama kemajuan
      Tetapi kalau sistem yang saya andalkan rusak hanya karena seseorang mengganti nama parameter atau karena sebagian standard library terlihat 'berantakan', itu sama sekali tidak menyenangkan

    • Kalau benar-benar ingin mengubah sesuatu, saya mungkin akan menukar sebagian control character dengan karakter yang lebih umum untuk sedikit menghemat ruang (kalau sampai kompatibilitas Unicode pun boleh dirusak). Sebagai format encoding karakter multibyte, bahkan jika dilihat secara terpisah pun, saya rasa ini hampir optimal

  • Saya sangat suka tautan playground UTF-8 ini (utf8-playground.netlify.app). Akan lebih bagus jika UI juga memungkinkan input code point langsung (saat ini sepertinya hanya bisa lewat URL). (Pembaruan: ternyata itu sudah bisa karena PR sudah di-merge)

    • Terima kasih atas kontribusinya, sekarang sudah di-merge dan langsung aktif
  • Kalau ingin menggali topik ini lebih dalam dan suka gaya seperti Advent of Code, ada beberapa puzzle tentang text encoding di i18n-puzzles. Itu membantu untuk benar-benar menginternalisasi cara kerja UTF-8 dan UTF-16

  • Saya menikmati tulisan ini, terima kasih. Saya juga merekomendasikan UTF-8, tetapi menurut saya itu baik hanya jika selalu dipakai bersama BOM. Tanpa itu, aplikasi tidak bisa tahu bahwa itu UTF-8 dan bisa melewatkan fakta bahwa penyimpanannya juga harus dalam UTF-8. Misalnya di Windows, jika Anda membuat dokumen teks baru dan file masih kosong, cukup adanya BOM akan membuat aplikasi mana pun otomatis paham bahwa saat nanti diedit/disimpan, file itu harus disimpan sebagai UTF-8. Tanpa BOM, sekalipun aplikasi mencoba mendeteksi encoding otomatis, hasilnya tidak akan pernah sepenuhnya andal, dan begitu karakter khusus seperti huruf beraksen ditambahkan, kebingungannya makin besar (editor bisa salah menebak bahasa, atau Notepad bisa mengubah encoding default setelah pembaruan). Jadi saya setuju memakai UTF-8, tetapi BOM harus menjadi pengaturan default wajib di OS/aplikasi