طول السلسلة النصية في Swift: ليس مجرد count، بل مغامرة كاملة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا "طول السلسلة النصية" ليس مجرد رقم؟

\n\n

تخيل أنك تكتب تطبيقًا لنظام iOS. يقوم المستخدم بإدخال اسمه في حقل نصي. الأمر بسيط؟ ماذا لو كان الاسم يحتوي على رموز تعبيرية (emojis) أو علامات تشكيل (مثل "café") أو أحرف صينية؟ فجأة، تتحول المهمة البسيطة "الحصول على طول السلسلة النصية" إلى تحقيق بوليسي مع عناصر Unicode.

\n\n

في Swift، العمل مع السلاسل النصية ليس مجرد عدّ للأحرف. إنها رحلة إلى عالم مجموعات الحروف الرسومية (grapheme clusters) و Unicode scalars وإرث Objective-C (نعم، NSString لا يزال معنا). إذا كنت قد بدأت للتو في تعلم Swift أو تنتقل من لغات أخرى، فهذه المقالة لك. سنشرح كيفية تحديد طول السلسلة النصية بشكل صحيح، وما هي المزالق التي تنتظرك، ومتى تستخدم count ومتى تستخدم NSString.

\n\n

هذه المادة مبنية على سؤال شائع حقيقي من Stack Overflow. آلاف المطورين تعثروا في هذا الموضوع. دعونا لا نكرر أخطائهم. هيا بنا!

\n\n
\n\n

1. الأساسيات: خاصية count في String

\n\n

الطريقة الأكثر وضوحًا للحصول على طول السلسلة النصية في Swift هي استخدام الخاصية count. إنها تُرجع عدد "الأحرف" (Character) في السلسلة النصية. ولكن ما هو الحرف بالنسبة لـ Swift؟

\n\n
let greeting = "مرحبًا بالعالم!"\nprint(greeting.count) // 12 (بما في ذلك المسافة وعلامة التعجب)
\n\n

كل شيء يعمل كما هو متوقع. لكن دعنا نعقد المهمة:

\n\n
let emojiString = "👨‍👩‍👧‍👦"\nprint(emojiString.count) // 1 (!!!)
\n\n

واحد؟ بجدية؟ بينما هي بصريًا عائلة مكونة من أربعة أفراد. ما الخدعة؟

\n\n

الإجابة تكمن في كيفية معالجة Swift للسلاسل النصية. count لا يحسب البايتات ولا أحرف Unicode الفردية، بل يحسب مجموعات الحروف الرسومية الممتدة (extended grapheme clusters). وهي عبارة عن تسلسل من واحد أو أكثر من Unicode scalars التي ينظر إليها المستخدم على أنها حرف واحد. الرمز التعبيري "عائلة" يتكون من عدة أجزاء، لكن Swift يعتبرها حرفًا واحدًا، لأن الإنسان يراها كذلك.

\n\n

هذا السلوك ليس خطأ، بل ميزة. تم تصميم Swift مع مراعاة التدويل. لو كان count يحسب كل Unicode scalar، فإن السلسلة النصية "café" (مع حرف é واحد) قد تعطي نتيجة مختلفة اعتمادًا على كيفية ترميز é: كحرف واحد U+00E9 أو كمجموعة e + علامة تشكيل (U+0065 + U+0301).

\n\n
let cafe1 = "café" // é كحرف واحد\nlet cafe2 = "cafe\\u{0301}" // e + علامة تشكيل\nprint(cafe1.count) // 4\nprint(cafe2.count) // 4 (على الرغم من وجود 5 scalars في السلسلة)
\n\n

يعتبر Swift كلا الخيارين كـ 4 أحرف، لأن المستخدم يرى "café" مكونة من 4 أحرف. هذا هو سحر مجموعات الحروف الرسومية.

\n\n
\n\n

2. نهج بديل: NSString وخاصية length

\n\n

أحيانًا تحتاج إلى الحصول ليس على عدد الأحرف "البشري"، بل على عدد وحدات ترميز UTF-16. على سبيل المثال، عند العمل مع واجهات برمجة تطبيقات قديمة (APIs) أو Core Text أو عند نقل البيانات بتنسيقات تتوقع الطول بـ UTF-16 (مثل بعض بروتوكولات الشبكات).

\n\n

هنا يأتي دور NSString — الفئة القديمة الجيدة من Objective-C. يتم ربط (bridge) السلاسل النصية في Swift تلقائيًا بـ NSString، لذا يمكنك استخدام خاصيته length:

\n\n
let swiftString = "👨‍👩‍👧‍👦"\nlet nsString = swiftString as NSString\nprint(nsString.length) // 11 (عدد وحدات UTF-16)
\n\n

لماذا 11؟ لأن الرمز التعبيري المركب "عائلة" يشغل 11 وحدة في UTF-16 (كل مكون يُرمَّز كزوج بديل (surrogate pair) أو كحرف منفصل). إذا كنت تعمل مع نص سيتم معالجته بواسطة مكتبات C قديمة أو APIs تتوقع NSString، فاستخدم length. لكن تذكر: هذا ليس عدد الأحرف التي يراها المستخدم.

\n\n

فارق بسيط آخر: NSString لا يأخذ في الاعتبار مجموعات الحروف الرسومية. بالنسبة للسلسلة النصية "café" (الخيار الثاني)، سيعيد length القيمة 5، بينما count يعيد 4. الخلاصة: اختر الم

المدونات

توصيات الكتب