طول النص
احسب النص بخمس طرق في آن واحد: الأحرف كما تعدّها بعينك مع تشكيلها، ونقاط الترميز (Python len)، ووحدات UTF-16 (JavaScript .length)، والكلمات والأسطر.
لماذا لسؤال «ما طول هذا النص؟» أكثر من إجابة
يبدو سؤالًا بديهيًا، لكن طول النص يعتمد كليًا على ما تعدّه. الإنسان يعدّ الأحرف التي يراها. قاعدة البيانات تعدّ المساحة التخزينية التي تشغلها القيمة. أما JavaScript فتعدّ شيئًا مختلفًا. وعندما تتباين هذه الأرقام — وهو ما يحدث كثيرًا مع الرموز التعبيرية والحروف المشكَّلة والكتابات غير اللاتينية — يصبح «الطول» الواحد مضللًا. تعرض هذه الأداة خمسة مقاييس ذات معنى جنبًا إلى جنب لتختار ما يهمّك في حالتك.
شرح المقاييس الخمسة
- الأحرف (الوحدات الرسومية / graphemes): ما يدركه الإنسان كحرف واحد. رمز تعبيري لعائلة مثل 👨👩👧، أو حرف مع علامة تشكيل مدمجة، يُحسب كواحد — وهذا هو العدّ الذي يطابق ما تراه بعينك.
- نقاط الترميز (code points): عدد قيم Unicode العددية. قد يتكوّن حرف مرئي واحد من عدة نقاط ترميز مدمجة، لذا يكون هذا الرقم غالبًا أكبر من عدد الوحدات الرسومية.
- وحدات UTF-16: القيمة التي تُبلغ عنها JavaScript من خاصية .length للسلسلة. الأحرف خارج المستوى المتعدد اللغات الأساسي (معظم الرموز التعبيرية) تشغل وحدتَي UTF-16 لكل منها.
- الكلمات: عدد الرموز الشبيهة بالكلمات، المكتشَفة بطريقة تراعي Unicode بدل التقسيم الساذج على المسافات.
- الأسطر: عدد الأسطر، مع احتساب كل فاصل سطر.
لماذا تكسر الرموز التعبيرية والتشكيل العدّ الساذج
النص الحديث مبني على Unicode، الذي يمثّل كثيرًا من الأحرف المرئية كتسلسلات لا كوحدات مفردة. رمز العلم التعبيري هو رمزا مؤشِّر إقليمي. ورمز العائلة هو عدة أشكال مرتبطة بأحرف «رابط بعرض صفري» غير مرئية. والحرف é قد يكون نقطة ترميز واحدة، أو حرف e عاديًا يتبعه تشكيل مدمج. كلٌّ منها يبدو حرفًا واحدًا لكنه قد يُحسب اثنين أو ثلاثة أو أكثر حسب المقياس. ولهذا بالضبط يؤدي العدّ اليدوي، أو الاعتماد على رقم واحد، إلى أخطاء برمجية.
أي عدّ ينبغي أن تستخدم؟
- حدود الأحرف التي يراها الناس (نبذة تعريفية، حقل بأسلوب التغريدة، معاينة رسالة نصية): استخدم «الأحرف» (الوحدات الرسومية)، لأنها تطابق ما يعدّه المستخدم.
- التعامل مع السلاسل في JavaScript أو الفهرسة أو التقطيع: استخدم وحدات UTF-16، فعليها تقوم .length وفهارس السلسلة.
- أحجام أعمدة قواعد البيانات: تحقّق مما إذا كان العمود يُقاس بالأحرف أم بالبايتات — فكثير منها محدود بالبايتات، وبايتات UTF-8 تختلف مجددًا عن كل هذه الأعداد.
- عدّ الكلمات أو الأسطر للمحرّرات وسهولة القراءة: استخدم مقياسَي «الكلمات» و«الأسطر».
ما الذي يسمّيه كل من Python وJavaScript «الطول»
اثنان من الأرقام الخمسة أعلاه هما تحديدًا سبب وصول معظم الناس إلى هذه الصفحة، وهما ينتميان إلى لغتين مختلفتين. فـ len() في Python و.length في JavaScript يوصفان معًا بأنهما طول السلسلة، لكنهما في أي شيء يتجاوز ASCII البسيط يعطيان إجابتين مختلفتين للنص نفسه في اللحظة نفسها. ولا أحد منهما مخطئ: كلٌّ منهما يعدّ وحدة مختلفة، وكلا الحقلين معروض أعلاه ليُقرأ الفارق بدل أن يُخمَّن.
- Python: تُعيد len() عدد نقاط ترميز Unicode، لأن السلسلة في Python 3 تسلسل من نقاط الترميز. انظر إلى حقل «نقاط الترميز» — هذا هو الرقم الذي ستعطيك إياه len().
- JavaScript: تُعيد .length عدد وحدات ترميز UTF-16، لأن سلسلة JavaScript محفوظة بترميز UTF-16. انظر إلى حقل «وحدات UTF-16». أما Array.from(text).length فيمرّ على السلسلة نقطة ترميز تلو الأخرى، ولذلك يتفق مع Python.
- ولا واحدة منهما تعدّ ما تراه. حقل «الأحرف» هو عدد الوحدات الرسومية، ولا يُنتجه أي طول مدمج في أي من اللغتين: تحتاج JavaScript إلى Intl.Segmenter ويحتاج Python إلى حزمة من طرف ثالث. وإذا كان الحد الذي تفرضه حدًّا سيقرؤه إنسان، فهذا هو الحقل المطلوب.
- و«الكلمات» و«الأسطر» ليستا من نصيب أي من اللغتين أيضًا. تستطيع كلتاهما تقريبهما بالتقسيم على المسافات وعلى فواصل الأسطر، وتبتعد كلتاهما عن الأعداد المعروضة هنا، التي تراعي Unicode — في نص يُكتب بلا مسافات، وفي علامات الترقيم، وفي فاصل سطر يُنهي النص.
المثال خلف زر تحميل مثال أسرع طريقة لرؤية الفارق. فرمز العائلة التعبيري فيه حرف واحد للقارئ، وخمس نقاط ترميز لـ Python، وثماني وحدات UTF-16 لـ JavaScript: ثلاث من نقاط الترميز الخمس تقع خارج المستوى المتعدد اللغات الأساسي وتكلّف وحدتين لكل منها، والرابطان غير المرئيين بينهما يكلّفان وحدة واحدة لكل منهما. فحقل يُسمح فيه بكتابة عشرين حرفًا يتّسع فعلًا لعشرين بالعين، بينما يرفض فحص ساذج مكتوب بأي من اللغتين نصًا يتّسع له الحقل.
الطول ليس هو الحجم بالبايت
ولا واحد من الأرقام الخمسة أعلاه عدد بايتات، والخلط بين الاثنين سهل. فالعدّ صفة للنص نفسه، أما الحجم بالبايت فصفة للنص بعد أن يُكتب بترميز ما، ولذلك تشغل السلسلة نفسها حجمًا في UTF-8 وحجمًا آخر في UTF-16. وما يُقاس بالبايت يُقاس بعد الترميز إذن، والترميز الذي وقع عليه الاختيار جزء من الإجابة.
- UTF-8 هو ما يستعمله كل شيء تقريبًا، وعرضه متغيّر: الحرف أو الرقم أو علامة الترقيم من ASCII بايت واحد، ومعظم الحروف اللاتينية ذات العلامات والحروف غير اللاتينية بايتان أو ثلاثة، وكثير من الرموز التعبيرية أربعة. فمئة حرف لا تساوي مئة بايت إلا حين تكون كلها من ASCII — والمئة نفسها باليونانية أو العبرية أو العربية أو الصينية تزن ضعفين إلى ثلاثة أضعاف.
- Python: تعدّ len() نقاط الترميز، فرمّز قبل أن تقيس. وتُعطي len(text.encode('utf-8')) عدد بايتات UTF-8، وذكر ترميز آخر في موضعه يعطي عددًا آخر، وهذا هو المقصود بالضبط.
- JavaScript: تعدّ .length وحدات UTF-16، فهي ليست عدد بايتات كذلك. وتُعطي new TextEncoder().encode(text).length عدد بايتات UTF-8 في المتصفح وفي Node على السواء، كما أن TextEncoder لا يُنتج إلا UTF-8، فلا ترميز يُذكر.
- تحقّق من الوحدة التي كُتب بها الحد قبل أن تعدّ شيئًا. فبعض محرّكات قواعد البيانات تُعرّف العمود بالأحرف وبعضها بالبايتات، وقيمة ترويسة HTTP وحجم الملف بالبايت، والقاعدة المكتوبة للبشر هي ما يعرضه حقل «الأحرف». فكل ما يُقاس بالأحرف موجود أعلاه بالفعل، وكل ما يُقاس بالبايت يحتاج إلى طول بعد الترميز.
ومتى صار عدد البايتات في يدك، فمحوّل أحجام البيانات هو موضع المتابعة: يحوّل عدد البايتات الخام إلى نظامَي الوحدات معًا — القائم على قوى الألف والقائم على قوى الاثنين — ويبيّن كيف تُبلّغ كل أداة على جهازك عن الحجم نفسه. تقف هذه الصفحة حيث يبدأ الترميز، وتبدأ تلك من عدد بايتات.
الأسئلة الشائعة
- أي عدّ يطابق طول السلسلة في JavaScript؟
- قيمة وحدات UTF-16 تطابق خاصية .length الخام في JavaScript. إذا كان كودك يقطّع السلاسل أو يفهرسها، فهذا هو الرقم الذي يحكم سلوكه.
- لماذا يُحسب رمز تعبيري واحد كعدة أحرف؟
- كثير من الرموز التعبيرية مكوَّنة من عدة نقاط ترميز Unicode مدمجة معًا. كوحدة رسومية (ما تراه) هو حرف واحد، لكن أعداد نقاط الترميز وUTF-16 له أكبر، ولهذا يُعرض كل مقياس على حدة.
- هل يحسب المسافات وفواصل الأسطر في النهاية؟
- نعم. يُحسب كل حرف تلصقه، بما في ذلك المسافات في البداية والنهاية وفواصل الأسطر. إذا بدت المجاميع أكبر من المتوقع، فتحقّق من وجود مسافات نهائية غير مرئية.
- هل يُرسَل نصي إلى خادم؟
- لا. كل العدّ يجري في متصفحك. النص الذي تكتبه أو تلصقه لا يغادر جهازك أبدًا.
- ما هي الوحدة الرسومية (grapheme)؟
- الوحدة الرسومية، أو «عنقود الوحدات الرسومية»، هي حرف كما يدركه المستخدم — أصغر وحدة يسمّيها القارئ حرفًا واحدًا، حتى لو كانت مكوَّنة من عدة نقاط ترميز.
- كيف تختلف البايتات عن هذه الأعداد؟
- تعتمد البايتات على الترميز. في UTF-8، حرف ASCII بايت واحد، ومعظم الأحرف المشكَّلة وغير اللاتينية بايتان أو ثلاثة، وكثير من الرموز التعبيرية أربعة. لا شيء من المقاييس الخمسة هنا هو عدّ بايتات، لذا للحقول المحدودة بالبايتات رمّز إلى UTF-8 أولًا.
أدوات ذات صلة
- محوّل أحجام البيانات
ليس أي من الأعداد هنا حجمًا بالبايتات: الحجم لا ينشأ إلا بعد ترميز النص. ومتى صار ذلك العدد بين يديك، تقرؤه تلك الصفحة بالوحدات العشرية والثنائية جنبًا إلى جنب، وتقول بماذا سيسميه كل نظام تشغيل.
- مُختبِر التعابير النمطية
اختبار التعابير النمطية — مطابقة واستبدال وتقسيم.
- مقارنة النصوص
قارن نصين — كل سطر وكلمة تغيّرا.
- فاحص محارف يونيكود
اعرف بالضبط من أي محارف يتألف النص.