سلاسل النصوص في Swift: لماذا تعمل substring بشكل مختلف عما اعتدت عليه؟

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

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

ابدأ الدورة

مقدمة: "المفاجأة" التي تنتظر كل مطور Swift

\n\n

تخيل الموقف: أنت تكتب كودًا بثقة في Swift، كل شيء يسير بسلاسة، وفجأة تحتاج إلى أخذ أول ثلاثة أحرف من سلسلة نصية. تكتب دون تفكير شيئًا مثل myString[0..<3] — وتحصل على... خطأ في الترجمة. هل هذا مألوف؟

\n\n

مرحبًا بك في عالم String substring في Swift. هذا أحد المواضيع التي تسبب ألمًا حقيقيًا للمبتدئين وحتى للمطورين ذوي الخبرة القادمين من لغات أخرى. سؤال "How does String substring work in Swift" على Stack Overflow حصد آلاف المشاهدات — وهذا ليس من قبيل الصدفة.

\n\n

في هذه المقالة، سنشرح لماذا Swift "غريب" في التعامل مع السلاسل الفرعية، وكيفية استخدام substring في Swift بشكل صحيح، وما هي الإمكانيات الجديدة التي ظهرت في الإصدارات الأخيرة. هيا بنا!

\n\n

لماذا لا يسمح Swift ببساطة بأخذ جزء من السلسلة النصية؟

\n\n

في معظم اللغات (Python, JavaScript, C#) السلسلة النصية هي مجرد مصفوفة من الأحرف. تريد سلسلة فرعية؟ اكتب str[0:3] واحصل على النتيجة. في Swift الأمر مختلف.

\n\n

السبب يكمن في التعامل مع Unicode. السلاسل النصية في Swift ليست مجرد بايتات أو أحرف ASCII. إنها تتكون من مجموعات حروف رسومية (grapheme clusters). الحرف الواحد الذي يراه المستخدم (مثل "é" أو "👍🏽") قد يشغل عدة Character أو حتى عدة Unicode.Scalar. تخيل أنك تقطع السلسلة النصية في منتصف هذه المجموعة — ستحصل على بيانات تالفة.

\n\n

لذلك يستخدم Swift المؤشرات (String.Index) بدلاً من الأعداد الصحيحة. هذا يضمن أنك لن تقطع حرفًا إلى نصفين أبدًا.

\n\n

الأساسيات: String.Index واستخراج سلسلة فرعية بسيطة

\n\n

لنبدأ بمثال أساسي. لنفترض أن لدينا سلسلة نصية:

\n\n
let fullString = "مرحبًا، Swift!"\n// نريد أخذ كلمة "Swift"\nlet startIndex = fullString.index(fullString.startIndex, offsetBy: 8)\nlet endIndex = fullString.index(fullString.startIndex, offsetBy: 13)\nlet substring = fullString[startIndex..<endIndex]\nprint(substring) // سيطبع: "Swift"
\n\n

لاحظ: substring من النوع Substring وليس String. هذا فرق مهم. Substring هي "نافذة" على السلسلة النصية الأصلية. إنها لا تنسخ البيانات، بل تشير فقط إلى جزء من ذاكرة السلسلة النصية الأصلية. هذا فعال للغاية، لكنه قد يؤدي إلى تسرب للذاكرة إذا كانت السلسلة النصية الأصلية كبيرة والفرعية صغيرة.

\n\n

لتحويل Substring إلى String كاملة، استخدم المُهيئ:

\n\n
let newString = String(substring)\nprint(newString) // "Swift"
\n\n

السكر النحوي: RangeExpression

\n\n

كتابة index(offsetBy:) في كل مرة أمر متعب. لحسن الحظ، يسمح Swift باستخدام صيغ أقصر باستخدام RangeExpression.

\n\n

العامل ... (ClosedRange)

\n
let str = "Hello, World!"\nif let range = str.range(of: "World") {\n    let worldSubstring = str[range]\n    print(worldSubstring) // "World"\n}
\n\n

النطاقات الجزئية (Partial Range)

\n

هل تريد أخذ كل شيء حتى مؤشر معين أو بعده؟ بسهولة:

\n
let text = "Swift Programming"\nlet prefix = text[..<text.index(text.startIndex, offsetBy: 5)]\nprint(prefix) // "Swift"\n\nlet suffix = text[text.index(text.startIndex, offsetBy: 6)...]\nprint(suffix) // "Programming"
\n\n

لاحظ: prefix و suffix هما أيضًا من النوع Substring.

\n\n

الطرق المضمنة: prefix, suffix, dropFirst, dropLast

\n\n

يوفر Swift طرقًا ملائمة تغنيك عن حساب المؤشرات يدويًا. تعيد هذه الطرق Substring.

\n\n
    \n
  • str.prefix(5) — أول 5 أحرف.
  • \n
  • str.suffix(3) — آخر 3 أحرف.

المدونات

توصيات الكتب