تريد اختصار تسمية طويلة إلى ثلاثة أجزاء، فتأخذ أول ثلاثة محارف. لكن ماذا عدَدت: وحدات التخزين، أم نقاط يونيكود، أم مجموعات يبقى فيها التشكيل مع الحرف؟ سنبني سلسلة قصيرة عن قصد، وننفذ ثلاث عمليات تبدو متشابهة بالاسم، ثم نحدد أيها يحقق الاتفاق الذي وضعناه للتسمية.
الخلاصة السريعة
- في مثالنا عشر وحدات UTF-16، وثماني نقاط يونيكود، وأربعة عناقيد.
- أول ثلاثة عناقيد تنتهي قبل X، عند موضع الوحدة 9.
- أول ثلاث وحدات تنتهي داخل ترميز الرمز الثاني.
- أول ثلاث نقاط لا تفصل ذلك الزوج الترميزي، لكنها تبتر الرمز المركب.
- نحفظ الأجزاء كما جاءت في الأصل؛ لا نحول التشكيل أو نطبع النص بصيغة أخرى.
اكتب وحدة الاختصار بوضوح
العنقود الكتابي الممتد يقارب المحرف المدرك، وقد يضم نقاطًا متعددة. وهو لا يساوي دائمًا الرسم الطباعي؛ وقد يحتاج التحرير تخصيصًا. [1]
اتفاقنا في هذا التمرين محدد: نأخذ أول ثلاثة أجزاء أعادها المقسّم عند اختيار مستوى العنقود. لا نقول «أول ثلاثة أحرف» ثم نترك البرنامج يختار معنى العدد. ونفصل الاختصار عن إضافة علامة الحذف؛ نريد أولًا بادئة صحيحة يمكن فحص نقاطها.
نعرض نموذجًا صغيرًا له بداية لاتينية ووسط رمزي ثم حرف عربي، حتى لا ينجح حل خاطئ لمجرد أن كل مدخلاته حروف منفردة. لم نحصل على السلسلة من اسم شخص أو رسالة خاصة؛ اخترنا مكوناتها لتظهر ثلاثة أنواع من الحدود في مثال واحد.
ابنِ السلسلة من نقاط معلنة
السلسلة هي Á👩🔬بُX، دون مسافات. تبدأ بـA ثم العلامة المركبة الحادة، تليهما المرأة وعلامة الوصل الصفرية والمجهر، ثم باء وضمة، ثم X. كتابة العلامات منفصلة في هذا الوصف تشرح التخزين؛ لا تطلب إدخال فراغ بينها.
تعد جافاسكربت طول String بوحدات UTF-16 ذات الستة عشر بت، وتبدأ فهارسها من صفر. ويمكن أن تمثل نقطة واحدة بزوج من هذه الوحدات؛ لذلك لا يساوي طول السلسلة بالضرورة عدد نقاطها. [3]
في سلسلتنا تحتاج المرأة وحدتين، والمجهر وحدتين، بينما يحتاج كل مكون آخر وحدة واحدة. نجمع 1 + 1 + 2 + 1 + 2 + 1 + 1 + 1 = 10. أما عد المكونات الثمانية المذكورة فلا يضم كل زوج مرتين؛ هذه هي نتيجة المرور على نقاط المثال.
| الجزء | النقاط | مجال الوحدات |
|---|---|---|
| Á | 0041 + 0301 | [0, 2) |
| 👩🔬 | 1F469 + 200D + 1F52C | [2, 7) |
| بُ | 0628 + 064F | [7, 9) |
| X | 0058 | [9, 10) |
القوس المربع يعني أن البداية داخلة، والقوس الدائري يعني أن النهاية مستبعدة. الجزء الثاني يأخذ الوحدات ذات الفهارس 2 و3 و4 و5 و6، ثم يبدأ التالي عند 7. فرق الطرفين خمسة، وليس عدد نقاط ذلك الجزء الذي يساوي ثلاثة.
نفّذ قصّين لا يحققان الاتفاق
إذا استعملنا text.slice(0, 3)، نحتفظ بوحدتي A وعلامتها ثم أول وحدة من زوج المرأة. النتيجة تنتهي بالقيمة D83D دون شريكتها DC69. لا نعتمد على شكل مربع أو رمز بديل قد يرسمه الخط؛ سجل الوحدات نفسه يكشف موضع القطع.
وإذا أخذنا أول ثلاث نقاط بـ[...text].slice(0, 3).join("")، نحصل على A مع علامتها ثم المرأة وحدها. صار الزوج الترميزي كاملًا، لكننا حذفنا الوصل والمجهر. هذه النتيجة تنتهي عند موضع الوحدة 4، وهو داخل المجال الثاني الذي أردنا إبقاءه كاملًا.
الاختبار «هل خرجت ثلاثة عناصر؟» لا يكفي؛ يمكن لكل طريقة أن تقول نعم بحسب وحدتها. ضع شرطًا أقوى لمهمتنا: نهاية الناتج واحدة من حدود الأجزاء الأصلية، وتسلسل النقاط في كل جزء محتفظ به مطابق لما كان عليه قبل القص.
اجمع الأجزاء بدل تخمين مواضعها
توفر Intl.Segmenter مستوى grapheme؛ ويحوي السجل segment للنص وindex لبدايته بوحدات السلسلة. [2]
const text = "A\u0301\u{1F469}\u200D\u{1F52C}\u0628\u064FX";
const split = new Intl.Segmenter("ar", {granularity: "grapheme"});
const parts = [...split.segment(text)];
function prefix(n) {
if (!Number.isInteger(n) || n < 0) throw new RangeError("n");
return parts.slice(0, n).map(p => p.segment).join("");
}
console.log(text.length, [...text].length, parts.length);
console.log(parts.map(p => p.index));
console.log(prefix(3) === text.slice(0, 9));
الناتج الأول هو 10 8 4، ثم قائمة البدايات [0, 2, 7, 9]، ثم true. تحققنا من تنفيذ هذه الشيفرة في بيئة Node المحلية، وحفظنا إصدار المحرك ونتائج الأجزاء؛ ليست هذه دعوى اختبار لجميع المتصفحات أو جميع إصدارات بيانات يونيكود.
تعيد prefix(3) المقاطع الثلاثة الأولى موصولة بلا فواصل جديدة، فتحتفظ بالبداية اللاتينية كاملة وبالرمز المركب وبالباء مع ضمتها، وتحذف X فقط. لا نأخذ أول ثلاثة سجلات ثم نعيد أرقام index كنص؛ نأخذ الحقل segment نفسه.
في اختبار الحدود، تعيد prefix(0) سلسلة فارغة، وتعيد prefix(4) الأصل كله، وكذلك الطلب 7 لأنه أكبر من عدد الأجزاء. أما العدد السالب أو الكسري فنرفضه صراحة في دالتنا؛ هذا قرار لواجهة المثال، وليس تصحيحًا صامتًا لمدخل مختلف.
افحص ما بقي قبل إضافة الزخرفة
لنتيجة الثلاثة سبع نقاط وتسع وحدات. طابق هذه الأعداد مع الجدول: نقطتان في الأول، وثلاث في الثاني، واثنتان في الثالث. ثم اجمع المجالات المتجاورة من صفر إلى 9؛ لا توجد فجوة أو تداخل. هكذا يرتبط اختبار الناتج بتكوين المدخل، لا بمجرد مقارنة لقطة تبدو سليمة.
إذا كانت التسمية في تطبيقك تحتاج علامة «…»، فقرر هل تدخل ضمن الحد أم تضاف بعده. مثالنا لا يضيفها. إدخال علامة ثم قص الناتج مرة ثانية بعدد وحدات قد يعيد الخطأ الذي أصلحناه؛ احتفظ بالبادئة المحسوبة كقيمة مستقلة قبل تنسيق العرض.
حد العناقيد لا يحد حجم البيانات أو العرض بالبكسل؛ وقد يجمع الخط عناقيد متعددة في رسم واحد. [1]
جرّب الكمية 2 على مدخلنا قبل النظر إلى الناتج: الحد المناسب هو 7، فتظل المرأة والوصل والمجهر معًا، وتغيب الباء وضمتها وX. ثم جرّب 1 فتقف عند 2. أما إذا انتهت نتيجة طلب ثلاثة أجزاء عند 4، فقد عددت نقاطًا في موضع احتجت فيه الأجزاء التي اتفقت عليها.
هذه العملية لا تراجع معنى التسمية ولا جودة العربية ولا صحة النص الوارد. إنها تحقق شرطًا محددًا على عينة محددة: قص بادئة عند حدود المقسّم مع إبقاء محتواها الأصلي. وسّع عينات الاختبار في تطبيقك وسجل البيئة، بدل تعميم نجاح هذه السلسلة القصيرة على كل واجهات التحرير.