Tidak suka iklan? Pergi Bebas Iklan Hari ini

UTF-8 dan Unicode Mengapa Emoji Itu Menghancurkan Database Anda

Diperbarui pada

Aplikasi Anda memasukkan emoji dan MySQL melemparkan pesan 'Nilai string salah'. Berikut alasannya — titik kode vs byte, kebohongan MySQL utf8 vs utf8mb4, pasangan surrogat JavaScript, dan cara memperbaikinya secara benar.

Aplikasi Anda memasukkan emoji dan MySQL melemparkan nilai string salah. Inilah sebabnya — kode titik vs byte, kebohongan MySQL utf8 vs utf8mb4, pasangan surrogate di JavaScript, dan cara memperbaikinya.
IKLAN · HAPUS?

Aplikasi Anda bekerja dengan baik. Lalu seorang pengguna mengetikkan emoji di bidang teks, dan MySQL melemparkan Incorrect string value: '\xF0\x9F\x98\x80' for column 'bio'. Atau mungkin emoji itu menghilang secara diam-diam. Atau seluruh INSERT gagal dan Anda kehilangan data. Semuanya karena karakter empat byte yang tidak diharapkan oleh kolom database Anda.

Ini bukan kekurangan MySQL atau bug PHP. Ini merupakan konsekuensi dari cara Unicode → pengkodean UTF-8 bekerja, dan setelah Anda memahaminya, Anda tidak akan terkejut lagi.

Kode titik vs byte: perbedaan sebenarnya

Unicode mengalokasikan setiap karakter dengan kode titik — sebuah angka. Huruf A adalah U+0041. Tanda Euro adalah U+20AC. Emoji 😀 adalah U+1F600. Itu adalah identitas abstrak karakter tersebut.

UTF-8 adalah pengkodean — cara menyimpan kode titik sebagai byte. Triknya adalah bahwa UTF-8 memiliki lebar variabel: ia menggunakan 1 hingga 4 byte tergantung pada nilai kode titik. Ini adalah cara agar UTF-8 tetap kompatibel dengan ASCII (semua karakter ASCII menggunakan 1 byte dalam UTF-8) sambil juga mengkodekan setiap karakter yang ada.

Aturan pengkodean:

  • U+0000 hingga U+007F (ASCII) → 1 byte
  • U+0080 hingga U+07FF (Latin diperluas, Arab, Hebrui, dll.) → 2 byte
  • U+0800 hingga U+FFFF (sebagian besar CJK, tanda baca, simbol) → 3 byte
  • U+10000 hingga U+10FFFF (emoji, skrip langka, simbol matematika) → 4 byte

Inilah sebabnya emoji 😀 (U+1F600) membutuhkan 4 byte: kode titiknya di atas U+FFFF.

Ukuran byte UTF-8: tabel referensi

Berikut ini adalah biaya aktual karakter umum dalam byte:

KarakterKeteranganTitik Kode UnicodeByte UTF-8 (hex)Jumlah Byte
AHuruf Latin kapital AU+0041411
éHuruf e dengan tanda akutU+00E9C3 A92
Tanda EuroU+20ACE2 82 AC3
Karakter Cina "tengah"U+4E2DE4 B8 AD3
😀Emoji senyumU+1F600F0 9F 98 804
🔥Emoji apiU+1F525F0 9F 94 A54
𝕳Huruf Fraktur Matematika HU+1D573F0 9D 95 B34

Untuk memverifikasi ini sendiri, gunakan Kalkulator Panjang String — ini menampilkan jumlah karakter dan jumlah byte untuk teks apa pun yang Anda tempelkan. Tempel 😀 dan Anda akan melihat 1 karakter tetapi 4 byte.

Kebodohan charset MySQL

Di sini para pengembang terjebak. MySQL memiliki charset yang disebut utf8. Terdengar benar. Tapi salah — charset MySQL utf8 hanya mendukung hingga 3 byte urutan. Emoji (4 byte) tidak didukung.

Charset UTF-8 penuh dalam MySQL adalah utf8mb4 (diperkenalkan di MySQL 5.5.3, dirilis tahun 2010). Jika kolom Anda menggunakan utf8 dan seseorang memasukkan emoji, MySQL akan secara diam-diam memotong data atau melemparkan:

Incorrect string value: '\xF0\x9F\x98\x80' for column 'bio' at row 1

Solusi:

-- Convert the table
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Or for a specific column
ALTER TABLE users MODIFY bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- And set your connection charset
SET NAMES utf8mb4;

Juga perbarui konfigurasi koneksi database aplikasi Anda. Di MySQL PDO:

$pdo = new PDO($dsn, $user, $pass, [
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]);

Jebakan VARCHAR(255)

VARCHAR(255) di MySQL berarti 255 karakter, bukan 255 byte — tetapi batas penyimpanan untuk satu baris dihitung dalam byte. Dengan utf8mb4, setiap karakter bisa mencapai hingga 4 byte, sehingga kolom tersebut menjamin hingga 1.020 byte. Ini penting ketika Anda menggunakan batas indeks default InnoDB (767 byte) untuk kolom varchar: VARCHAR(255) Masalah pasangan surrogate di JavaScript

-- This fails on older MySQL with default innodb_large_prefix=OFF
CREATE INDEX idx_email ON users (email);  -- email is VARCHAR(255) utf8mb4

-- Fix: use a prefix index
CREATE INDEX idx_email ON users (email(191));  -- 191 * 4 = 764 bytes, under 767

-- Or enable large prefixes (MySQL 5.7+, on by default in 8.0)
-- Set innodb_large_prefix = ON in my.cnf

JavaScript menggunakan UTF-16 secara internal, bukan UTF-8. Dan UTF-16 memiliki pengkodean multi-unit sendiri untuk kode titik di atas U+FFFF:

pasangan surrogate — dua unit kode 16-bit yang bersama-sama mewakili satu karakter. Ini berarti

di JavaScript menghitung unit kode UTF-16, bukan karakter: String.length Untuk operasi string yang membutuhkan kesadaran karakter, gunakan operator penyebaran atau

'😀'.length        // → 2 (two UTF-16 surrogate code units)
[...'😀'].length   // → 1 (spread operator uses Unicode code points)

// Checking the character at index 0
'😀'[0]            // → '\uD83D' (the high surrogate, not the emoji)
'😀'.codePointAt(0) // → 128512 (0x1F600, correct)

Contoh emoji keluarga layak dipertimbangkan. Intl.Segmenter:

// Count actual grapheme clusters
const segmenter = new Intl.Segmenter();
const chars = [...segmenter.segment('👨‍👩‍👧‍👦')];
chars.length     // → 1 (family emoji is one grapheme cluster)
'👨‍👩‍👧‍👦'.length  // → 11 (UTF-16 code units)

terdiri dari empat emoji yang dihubungkan oleh Zero Width Joiners (U+200D). Sebuah pendekatan yang tidak cermat 👨‍👩‍👧‍👦 menghasilkan 11. Jumlah cluster grafem aktual: 1. Ini penting jika Anda mengimplementasikan batas karakter — batas yang didasarkan pada .length akan berperilaku tidak sesuai ketika pengguna mengetik urutan emoji. String.length Cara memeriksa pengkodean secara praktis

di PHP menghitung byte, bukan karakter. Ini sering menangkap pengembang PHP saat berurusan dengan string multi-byte — string 10 emoji akan melaporkan panjang 40. Gunakan

Ular piton

s = '😀'
print(len(s))                    # 1 (Python 3 counts code points)
print(len(s.encode('utf-8')))    # 4 bytes
print(s.encode('utf-8').hex())   # f09f9880

PHP

$s = '😀';
echo strlen($s);          // 4 (bytes, not characters)
echo mb_strlen($s);       // 1 (characters)
echo mb_strlen($s, '8bit'); // 4 (bytes, explicit)

strlen() ketika Anda peduli terhadap jumlah karakter. mb_strlen() Pemeriksaan cepat

MySQL

-- Check charset of a table
SHOW CREATE TABLE users\G

-- Check charset of a specific column
SELECT CHARACTER_SET_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'users' AND COLUMN_NAME = 'bio';

-- Character count vs byte count
SELECT CHAR_LENGTH('😀'), LENGTH('😀');
-- → 1, 4

Jika Anda ingin melihat perbedaan antara byte dan karakter untuk teks apa pun tanpa menulis kode,

mengatasinya secara instan — tempelkan teks apa pun dan itu menampilkan jumlah karakter, jumlah kata, dan jumlah byte secara bersamaan. Kalkulator Panjang String Daftar pemeriksaan bug pengkodean

Charset MySQL:

  • Apakah itu ? Periksa dengan utf8mb4, bukan utf8Koneksi MySQL: SHOW CREATE TABLE.
  • Apakah aplikasi Anda mengirimkan ? Periksa DSN atau konfigurasi koneksi Anda. SET NAMES utf8mb4PHP strlen vs mb_strlen:
  • Apakah Anda menggunakan fungsi penghitungan byte di mana Anda membutuhkan penghitungan karakter? JavaScript .length:
  • Apakah Anda menghitung unit kode di mana Anda membutuhkan cluster grafem? Header HTTP:
  • Apakah respons Anda mengirimkan File pengkodean: Content-Type: text/html; charset=utf-8?
  • Apakah file sumber dan dump SQL disimpan sebagai UTF-8 tanpa BOM? UTF-8 dan Unicode: Mengapa Emoji Itu Menghancurkan Database Anda 2
Ingin bebas iklan? Bebas Iklan Hari Ini

Instal Ekstensi Kami

Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat

Ke Ekstensi Chrome Ke Ekstensi Tepi Ke Ekstensi Firefox Ke Ekstensi Opera

Papan Skor Telah Tiba!

Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!

IKLAN · HAPUS?
IKLAN · HAPUS?
IKLAN · HAPUS?

Pojok Berita dengan Sorotan Teknologi

Terlibat

Bantu kami untuk terus menyediakan alat gratis yang berharga

Belikan aku kopi
IKLAN · HAPUS?