مقياس طول السلسلة النصية

عند لصق سلسلة نصية، يُبلغ الأداة عن طولها بأربع طرق مختلفة: .length على نمط جافا سكريبت (وحدات رمزية في تنسيق UTF-16)، ونقاط رموز يونيكود، ومجموعات الحروف (ما يراه المستخدمون كحرف واحد)، وبايتات UTF-8 (ما تخزنه عمليًا عمود قاعدة البيانات). تختلف هذه القيم بالنسبة لأي سلسلة تحتوي على رموز إيموجي أو أعلام أو علامات دمج، وهذه الاختلافات هي مصدر العديد من الأخطاء البرمجية.

أربع طرق لقياس طول السلسلة النصية

  1. 1

    وحدات رمز UTF-16

    ما يُرجعه `str.length` في لغة جافا سكريبت: القيمة 1 تُستخدم للغالبية العظمى من الحروف، بينما تُستخدم القيمة 2 لأي نقطة رمزية تتجاوز نطاق U+FFFF (مثل الرموز التعبيرية والخطوط القديمة).

  2. 2

    نقاط الكود

    واحد لكل قيمة مقياسية في يونيكود؛ يحتوي كل إيموجي على قيمة واحدة، بينما تحتوي تسلسلات ZWJ على عدة قيم.

  3. 3

    مجموعات الجرافيم

    ما يُسمّيه المستخدم "حرفًا واحدًا": `🇺🇸` يتكوّن من نقطتين رمزيتين لكنه يمثّل جرافيمًا واحدًا، وكذلك `n̈` يتكوّن من نقطتين رمزيتين لكنه يمثّل جرافيمًا واحدًا أيضًا.

  4. 4

    بايتات UTF-8

    ما يخزنه قاعدة البيانات الخاصة بك: ASCII: 1 بايت لكل رمز؛ القياس الأوروبي الشائع: 2 بايت؛ CJK: 3 بايت؛ معظم الرموز التعبيرية (emoji): 4 بايت.

أمثلة

السلسلة النصية طول السلسلة النصية (JS .length) عدد نقاط الكود الحروف المفردة بايتات UTF-8
hello 5 5 5 5
café 4 4 4 5
😀 2 1 1 4
🇺🇸 4 2 1 8
👨‍👩‍👧 (عائلة) 8 5 1 18
n̈ (n + علامة نقطتين فوقيتين) 2 2 1 3

لماذا تختلف الأرقام

السلاسل النصية في JavaScript تعتمد على تنسيق UTF-16، ولذلك تُخزَّن أي نقطة رمزية تتجاوز U+FFFF كزوج بديل مكوّن من وحدتين رمزيتين في UTF-16. ولهذا تكون "😀".length === 2. يكره المستخدمون هذا الأمر لأنهم يعتبرون 😀 حرفًا واحدًا.

تذهب الجرافيمات إلى أبعد من ذلك: فعَلَم الدولة يتكوّن من نقطتين رمزيتين تدلّان على المنطقة، لكنّ المستخدم يراهما معًا عَلَمًا واحدًا. ففي JavaScript تكون "🇺🇸".length === 4، وهو ما يبدو غير منطقي، لكنه كذلك بالفعل. وللمرور على الجرافيمات تباعًا، استخدم Intl.Segmenter (الحديثة)، أو مكتبة grapheme-splitter، أو عالِجها يدويًا.

بايتات UTF-8: ما تخزّنه قاعدة بياناتك

بالنسبة لعمود من النوع VARCHAR(255):

  • في MySQL utf8 (وهو فعليًا utf8mb3)، يمثّل الرقم 255 عدد البايتات. تستهلك café خمسة بايتات، وبالتالي يمكن تخزين 51 نسخة منها.
  • في MySQL utf8mb4 (تنسيق UTF-8 الحقيقي الذي يشمل الرموز التعبيرية)، يظل العدّ بالبايتات، لكن الترميز يدعم التسلسلات المكوّنة من أربعة بايتات.
  • في Postgres VARCHAR(255)، يمثّل الرقم 255 عدد الحروف (نقاط الكود)، وليس البايتات.
  • في SQL Server VARCHAR، يختلف العدّ حسب نظام الترتيب (collation)؛ أما NVARCHAR فيَحسب وحدات UCS-2 المكوّنة من بايتين لكل وحدة.

إذا حدّدت أحجام حقول قاعدة البيانات وفقًا للحد الأقصى لعدد الحروف المرئية للمستخدم، فاستخدم قيمة «عدد البايتات × 4» لضمان الأمان في أعمدة UTF-8؛ إذ قد يستهلك كل جرافيم ما يصل إلى 4 بايتات لكل نقطة رمزية، مع زيادة إضافية ناتجة عن تسلسلات ZWJ.

عدّ الأحرف على نمط Twitter

يَحسب Twitter التغريدة وفق قاعدة مخصّصة: نقاط الكود، لكن الرموز التعبيرية (emoji) والحروف التصويرية CJK تُحتسب كل منها بحرفين. يمكن أن تحتوي تغريدة مكوّنة بالكامل من أحرف ASCII على 280 حرفًا؛ أما التغريدة التي تحتوي على 140 رمزًا تعبيريًا فيبلغ حدّها الأقصى 140 «حرفًا».

عدّ الرسائل النصية القصيرة (SMS): 160 حرفًا في نظام GSM ذي 7 بتات. وأي حرف غير متوافق مع GSM (مثل á وé وñ أو الرموز التعبيرية) يُحوّل الترميز إلى UCS-2، فيقل طول الرسالة إلى 70 حرفًا.

استخدامات عملية

  • تحديد حجم أعمدة قاعدة البيانات، تحقّق من عدد البايتات قبل كتابة عملية ترحيل (migration).
  • فرض حصص واجهات برمجة التطبيقات (API)، تَحسب معظم الواجهات البايتات، لا الحروف.
  • التحقق من صحة النماذج، اعرض للمستخدم عدد أحرف دقيقًا يطابق توقعاته (الجرافيمات).
  • تصحيح أخطاء الأداء، لماذا يتكرّر هذا التعبير النمطي (regex) ببطء؟ لأن طول المدخل بوحدات الكود يفوق طوله بالجرافيمات بثلاثة أضعاف.

الأسئلة الشائعة

هذا الرمز التعبيري يمثل تسلسلًا من نوع ZWJ يجمع بين عدة نقاط رمزية (على سبيل المثال، رمز عائلي يتكون من 7 نقاط رمزية). يُحتسبه تويتر على أنه حرفان وفق قاعدة وزن يونيكود؛ بينما يعرض محررك رمزًا واحدًا فقط. كلا الحالتين صحيحة من الناحية التقنية، لكنهما تقيسان أمورًا مختلفة.

في قواعد البيانات التي تستخدم تنسيق UTF-8، يُوصى باستخدام 4 بايتات لكل حرف متوقع لضمان الأمان. يجب أن يكون حقل يحتوي على 100 حرف (الغرافيم) من نوع VARCHAR بحجم 400 بايت؛ أما إذا كانت قاعدة البيانات تدعم أحرفًا مثل تلك المستخدمة في Postgres، فيُستخدم نوع VARCHAR بحجم 100 بايت مع إعدادات التعامل مع مجموعة الأحرف المناسبة.

في JavaScript يعتمد الأمر على شكل التطبيع (التركيب)؛ فالشكل المركّب NFC (U+00E1) طوله واحد، بينما الشكل المُفكّك NFD (U+0061 + U+0301) طوله اثنان. الحرف المرئي نفسه، لكن تسلسلات البايتات مختلفة.

رموز التعبير الخاصة بالعائلة والمهنة عبارة عن تسلسلات طويلة من نوع ZWJ. عند استخدام خطوط لا تدعم هذه الرموز بشكل كامل، يتم عرض كل نقطة رمزية على حدة كرمز منفصل، وبالتالي يصبح 👨‍💻 هو في الواقع 👨 + 💻. ويتم إصلاح هذه المشكلة عند ترقية خط نظام التشغيل.

أدوات ذات صلة

رمز أقل من أو يساوي

انسخ علامة ≤ ورموز المقارنة الرياضية المرتبطة بها. يشمل ذلك رموز Unicode وكيانات HTML وترميز LaTeX للجداول الحسابية والأوراق البحثية وصفحات الويب.

نسخ رموز القلب

انسخ مجموعة منتقاة من رموز القلب والإيموجي الملون والزخارف. اطّلع على نقاط ترميز يونيكود الدقيقة قبل اللصق.

أداة كشف الأحرف غير المرئية

اكتشف وحذف الفواصل ذات العرض الصفري، والرموز اليونيكية المخفية، والمنفصلات غير المرئية من أي نص يحتوي على بيانات نقاط الكود والموقع.

مترجم الإيموجي

اكتب نصا إنجليزيا، وستحصل كل كلمة من القائمة المدمجة على إيموجي مناسبة. انسخ النتيجة كنص يحتوي على إيموجي.

مولّد خطوط إنستغرام

ولّد "خطوط" Unicode (عريض، مائل، مخطوط، أحادي المسافة، فراكتور) تُلصق مباشرة في نبذة إنستغرام واسمك وتعليقاتك.

مولد النص العكسي

اقلب النص رأسًا على عقب باستخدام رموز يونيكود التي تبدو كأحرف معكوسة، لاستخدامها في السير الذاتية، والتعليقات التوضيحية، والمنشورات الاجتماعية التي تلفت الانتباه.

الأداة متاحة بلغات أخرى