UTF-8 و Unicode لماذا احتوَى هذا الإيموجي على قاعدة بياناتك
أدخلت تطبيقك أيقونة وانهار MySQL بـ "مُدخل سلسلة غير صحيح". إليك السبب — الفرق بين نقاط الكود والبُيتات، الاحتيال في MySQL utf8 مقابل utf8mb4، أزواج المُستندات في JavaScript، وكيفية إصلاحه بشكل فعلي.
كان تطبيقك يعمل بشكل جيد. ثم أدخل مستخدم علامة تعبير في حقل نصي، وانفجرت MySQL Incorrect string value: '\xF0\x9F\x98\x80' for column 'bio'. أو ربما اختفى العلامة التعبيرية بشكل سري. أو فشل الإدخال بالكامل وفقدت البيانات. وكل ذلك بسبب حرف بطول 4 بايت لم يُتوقع من عمود قاعدة البيانات.
هذا ليس خللًا في MySQL أو خطأ في PHP. إنه نتيجة لطريقة عمل تشفير Unicode إلى UTF-8، وعندما تفهمها، لن تُدهش من جديد.
النقطة المميزة مقابل الأحرف: الفرق الحقيقي
يُخصص Unicode لكل حرف رقمًا — النقطة الكودية. الحرف A هو U+0041. علامة اليورو هي U+20AC. العلامة 😀 هي U+1F600. هذه هي الهوية المجردة للحرف. يُعد UTF-8
— طريقة لتخزين النقطة المميزة كأحرف. الميزة هي أن UTF-8 متغير الطول: يستخدم 1 إلى 4 أحرف حسب قيمة النقطة المميزة. وهكذا يبقى متوافقًا مع ASCII (كل الأحرف في ASCII هي 1 حرف في UTF-8) مع تمثيل كل حرف موجود. ترميز قواعد التشفير:
U+0000 إلى U+007F (ASCII) → 1 حرف
- U+0080 إلى U+07FF (متوسّط اللاتيني، العربي، الهيبي، إلخ) → 2 حرف
- U+0800 إلى U+FFFF (أغلب الحروف الصينية، التعبيرات، الرموز) → 3 أحرف
- U+10000 إلى U+10FFFF (الرموز التعبيرية، السجلات النادرة، الرموز الرياضية) → 4 أحرف
- هذا هو السبب الذي يجعل علامة التعبير 😀 (U+1F600) تأخذ 4 أحرف: لأن رقم النقطة المميزة يتجاوز U+FFFF.
أحجام الأحرف في UTF-8: جدول مرجعي
إليك ما يكلفه الأحرف الشائعة بالحروف:
أحرف UTF-8 (الهكسادسِم)
| حرف | وصف | نقطة رمز يونيكود | الحروف اللاتينية الكبيرة A | عدد البايتات |
|---|---|---|---|---|
| أ | U+0041 | é | 41 | 1 |
| الحروف اللاتينية مع مائل | U+00E9 | C3 A9 | علامة اليورو | 2 |
| € | U+20AC | E2 82 AC | متوسط | 3 |
| الحروف الصينية "متوسط" | U+4E2D | E4 B8 AD | الوجه المبتسم | 3 |
| 😀 | U+1F600 | F0 9F 98 80 | الحريق | 4 |
| 🔥 | U+1F525 | F0 9F 94 A5 | 𝕳 | 4 |
| الحروف الرياضية الفرانتسية H | U+1D573 | F0 9D 95 B3 | لإثبات ذلك بنفسك، استخدم | 4 |
— يعرض عدد الأحرف والحجم بالحروف لنص أي نص تضعه. ضع حاسبة طول السلسلة وسترى 1 حرف لكن 4 أحرف. 😀 الخطأ في charset في MySQL
هنا يُصدم المطورون. يحتوي MySQL على نوع من التشفير يُسمى
. يبدو منطقيًا. لكنه خاطئ — يدعم MySQL فقط تسلسلات بطول 3 أحرف. لا يدعم الرموز التعبيرية (بطول 4 أحرف). utf8ال_CHARSET الكامل لـ UTF-8 في MySQL هو utf8 (مُدخل في MySQL 5.5.3، تم الإصدار في 2010). إذا استخدمت عمودك
وأدخل شخص رمز تعبير، فإن MySQL إما أن يُقصي البيانات بشكل سري أو يُنذر: utf8mb4 أيضًا، تأكد من إعدادات الاتصال مع قاعدة البيانات في MySQL PDO: utf8 الصعوبة في VARCHAR(255)
Incorrect string value: '\xF0\x9F\x98\x80' for column 'bio' at row 1
أوقف السلاسل الأصلية قبل أي تحليل ومرّر هذه السلاسل إلى دالة التحقق. في إكسبريس جي، قم بتسجيل المسار باستخدام
-- 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;
في MySQL تعني 255
$pdo = new PDO($dsn, $user, $pass, [
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]);
أحرف
VARCHAR(255) ، وليس 255 حرفًا — لكن الحد المسموح به لسطر واحد يُحسب بالحروف. مع ، يمكن أن يأخذ كل حرف حتى 4 أحرف، لذا يحتفظ العمود بحد أقصى يبلغ 1020 حرفًا. هذا مهم عندما تستخدم حد المُسبق في إنشاء مؤشرات في InnoDB (767 حرفًا) لعمود VARCHAR:المسألة المتعلقة بزوج الرموز في JavaScript utf8mb4يستخدم JavaScript التشفير UTF-16 داخليًا، وليس UTF-8. ويملك UTF-16 تشفير متعدد الوحدات لقيم النقطة المميزة فوق U+FFFF: VARCHAR(255) أزواج المُسجّل
-- 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
— وحدتين من 16 بت يمثلان معاً حرفًا واحدًا.
هذا يعني أن في JavaScript يُعدّ وحدات التشفير UTF-16، وليس الأحرف: لأي عمليات على النص التي تحتاج إلى تعرف الأحرف، استخدم مُوزع المُنحى أو
النوع المميز للرموز التعبيرية يستحق التوقف عليه. String.length مكوّن من 4 رموز متصلة بـ Zero Width Joiners (U+200D). يُعطي تحليل بسيط
'😀'.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)
عدد 11. الأعداد الفعلية للوحدات المُجمعة: 1. هذا مهم إذا كنت تُعدّ حدود للحروف — حد مبني على 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)
سيتصرف بشكل غير متوقع عندما يكتب المستخدم تسلسلات من الرموز التعبيرية. 👨👩👧👦 كيفية التحقق من التشفير عمليًا .length في PHP يُعدّ الأحرف، وليس الأحرف. هذا يُصدم المطورين في PHP باستمرار عند التعامل مع النصوص المتعددة الأحرف — سلسلة من 10 رموز تُظهر طولًا يبلغ 40. استخدم String.length عندما تحتاج إلى عدد الأحرف.
التحقق السريع
بايثون
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() إذا كنت ترغب في رؤية الفرق بين الأحرف والحجم للنص دون كتابة كود، فإن mb_strlen() يتعامل معه فورًا — ضع أي نص وسترى عدد الأحرف، عدد الكلمات، وعدد الأحرف بجانب بعضها.
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
قائمة التحقق من الأخطاء في التشفير
نوع التشفير في MySQL: حاسبة طول السلسلة هل هو
؟ تحقق من
- الاتصال مع MySQL: هل ترسل تطبيقك
utf8mb4القيم المنطقية تبقى كنصوص.utf8؟ تحقق من مصادر الاتصال أو إعدادات الاتصال.SHOW CREATE TABLE. - الطول في PHP مقابل mb_strlen: هل تستخدم وظائف تُحسب الأحرف حيث تحتاج إلى عدد الأحرف؟
SET NAMES utf8mb4الطول في JavaScript: - هل تُحسب الوحدات في التشفير حيث تحتاج إلى وحدات المجموعات؟ أوامر الرسائل:
- هل تُرسل ردًا يحتوي على مصدر الملفات:
- هل تم حفظ ملفات المصدر ونسخ SQL كـ UTF-8 بدون BOM؟ UTF-8 و Unicode: لماذا توقفت قاعدة البيانات عن العمل بسبب الرمز التعبيري 2
Content-Type: text/html; charset=utf-8? - UTF-8 و Unicode: لماذا توقفت قاعدة البيانات عن العمل بسبب الرمز التعبيري 1 هل تم حفظ ملفات مصدرك ونسخ الملفات SQL بصيغة UTF-8 بدون BOM؟
تثبيت ملحقاتنا
أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع
恵 وصلت لوحة النتائج!
لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!
