النصوص في Swift: String وأهم دوالها
عنوان الشاشة، واسم المستخدم، ونص الإشعار: تطبيقات iPhone تتعامل مع النصوص في كل مكان. تعرّفت على String وعلى إدراج القيم بـ \() في درس المتغيرات. في هذا الدرس ستتعلم أهم دوال النصوص في Swift، وستفهم لماذا تتعامل Swift مع الحروف بطريقة مختلفة قليلاً عن بقية اللغات.
بعض دوال هذا الدرس تأتي من مكتبة Apple الأساسية، فابدأ ملفك بهذا السطر:
import Foundationطول النص وحروفه
Section titled “طول النص وحروفه”let word = "Swift"print(word.count)print(word.isEmpty)print(word.uppercased())print(word.lowercased())print(String(word.reversed()))
for letter in "عمر" { print(letter)}5falseSWIFTswifttfiwSعمرcount: عدد الحروف، ويعمل بشكل صحيح مع العربية أيضاً.isEmpty: هل النص فارغ""؟uppercased()وlowercased(): نسخة جديدة بحروف كبيرة أو صغيرة.reversed()تُرجع الحروف معكوسة، وString(...)تجمعها في نص من جديد.
تخيّل النص مثل عقد من الخرز: كل خرزة حرف، والحلقة for تمر عليها واحدة واحدة.
let و var مع النصوص
Section titled “let و var مع النصوص”النص المعرَّف بـ let ثابت. أما المعرَّف بـ var فيمكن تعديله في مكانه:
var greeting = "مرحباً"greeting += " يا عمر"greeting.append("!")print(greeting)
let line = String(repeating: "=", count: 12)print(line)مرحباً يا عمر!============أول النص وآخره
Section titled “أول النص وآخره”let fileName = "photo.png"print(fileName.prefix(5))print(fileName.suffix(3))print(fileName.dropFirst(6))print(fileName.dropLast(4))print(fileName.hasPrefix("photo"))print(fileName.hasSuffix(".jpg"))photopngpngphototruefalseprefix(n)وsuffix(n): أول أو آخرnحروف.dropFirst(n)وdropLast(n): النص بعد حذفnحروف من أوله أو آخره.hasPrefixوhasSuffix: هل يبدأ النص أو ينتهي بهذا الجزء؟
لماذا لا يعمل word[0]؟
Section titled “لماذا لا يعمل word[0]؟”في أغلب اللغات تكتب word[0] لأخذ الحرف الأول. في Swift هذا خطأ ترجمة! السبب أن الحرف الواحد الظاهر على الشاشة قد يتكون من عدة أجزاء في الذاكرة (كالحروف المشكولة والرموز التعبيرية)، فلا تستطيع Swift القفز إلى الحرف رقم n مباشرة. لذلك تستخدم نوعاً خاصاً للمواضع اسمه String.Index:
if let at = email.firstIndex(of: "@") { let user = email[..<at] let domain = email[email.index(after: at)...] print(user) print(domain)}
let letters = Array("Swift")print(letters[0])print(letters[4])omarexample.comStfirstIndex(of:): تُرجع موضع أول ظهور للحرف كقيمة اختيارية، ولهذا فتحناها بـif let.email[..<at]: من البداية إلى ما قبل الموضع. وemail[...]بعده: من الموضع إلى النهاية.index(after:): الموضع التالي مباشرة.- إذا احتجت الوصول بالأرقام كثيراً، حوّل النص إلى مصفوفة حروف بـ
Array(...).
البحث والاستبدال والتنظيف
Section titled “البحث والاستبدال والتنظيف”let text = " Hello Swift "let clean = text.trimmingCharacters(in: .whitespaces)print("[\(clean)]")print(clean.contains("Swift"))print(clean.contains("Java"))print(clean.replacingOccurrences(of: "Swift", with: "World"))[Hello Swift]truefalseHello WorldtrimmingCharacters(in: .whitespaces): تحذف المسافات من الطرفين. استخدمها دائماً مع ما يكتبه المستخدم.contains: هل يحتوي النص على هذا الجزء؟replacingOccurrences(of:with:): تستبدل كل ظهور للنص الأول بالثاني.
كلها تُرجع نصاً جديداً ولا تغيّر الأصلي.
المقارنة
Section titled “المقارنة”let a = "swift"let b = "SWIFT"print(a == b)print(a == b.lowercased())print("apple" < "banana")falsetruetrue== تقارن محتوى النصين، والمقارنة حساسة لحالة الحروف. لتجاهل الحالة وحّد النصين بـ lowercased() أولاً. و < تقارن أبجدياً.
التقسيم والدمج
Section titled “التقسيم والدمج”let csv = "تفاح,موز,عنب"let fruits = csv.components(separatedBy: ",")print(fruits.count)print(fruits[1])print(fruits.joined(separator: " - "))
let sentence = "تعلّم البرمجة ممتع جداً"print(sentence.split(separator: " ").count)3موزتفاح - موز - عنب4components(separatedBy:): تقطع النص عند الفاصل وتُرجع مصفوفة[String].joined(separator:): تدمج مصفوفة نصوص في نص واحد.split(separator:): مثل الأولى لكنها تُرجع[Substring]وتتجاهل الأجزاء الفارغة.
النصوص متعددة الأسطر
Section titled “النصوص متعددة الأسطر”لكتابة نص طويل على عدة أسطر، استخدم ثلاث علامات تنصيص:
let name = "ليلى"let message = """ مرحباً \(name)، تم تسجيلك بنجاح. رقم طلبك: \(100 + 23) """print(message)مرحباً ليلى،تم تسجيلك بنجاح.رقم طلبك: 123موضع علامات الإغلاق """ يحدد الإزاحة التي تُحذف من أول كل سطر، فيبقى كودك مرتباً والنص نظيفاً.
من نص إلى رقم
Section titled “من نص إلى رقم”ما يكتبه المستخدم في حقل الإدخال يصلك نصاً دائماً. التحويل بـ Int(...) يُرجع قيمة اختيارية، لأن النص قد لا يكون رقماً:
let inputs = ["42", "abc", "7"]for input in inputs { if let number = Int(input) { print("العدد \(number) وضعفه \(number * 2)") } else { print("\(input) ليس رقماً") }}
let price = Double("19.5") ?? 0print(price + 0.5)print("العمر: " + String(15))العدد 42 وضعفه 84abc ليس رقماًالعدد 7 وضعفه 1420.0العمر: 15لا استثناءات ولا انهيار: إما رقم، أو nil تتعامل معه بـ if let أو ??. و String(15) تحوّل الرقم إلى نص.
جرّب بنفسك
Section titled “جرّب بنفسك”- خزّن جملة في ثابت، واطبع عدد حروفها وعدد كلماتها.
- خزّن اسم ملف مثل
report.pdf، واستخرج الامتداد بعد النقطة باستخدامcomponents(separatedBy:)وlast. - اكتب دالة
initials(of fullName: String) -> Stringتُرجع الحرف الأول من كل كلمة:"Omar Ali"تصبح"OA". - تحدٍّ: اكتب دالة
isPalindrome(_ word: String) -> Boolتُرجعtrueإذا كانت الكلمة تُقرأ من الجهتين بنفس الشكل مثلlevel، مع تجاهل حالة الحروف.
أخطاء شائعة
Section titled “أخطاء شائعة”- الوصول برقم:
word[0]خطأ ترجمة في Swift. استخدمword.firstأوprefixأو حوّل النص إلىArray. - دمج نص مع رقم:
"العمر: " + 15خطأ، فالنوعان مختلفان. استخدم الإدراج"العمر: \(15)"أوString(15). - نسيان أن التحويل اختياري:
Int("12") + 1خطأ ترجمة، لأن الناتجInt?. افتحه بـif letأو??. - تجاهل القيمة المُرجعة:
text.uppercased()وحدها لا تغيّرtext. خزّن النتيجة في ثابت جديد.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما ناتج "Hello".prefix(2)؟
prefix(2) تأخذ أول حرفين من النص.
سؤال
ما قيمة Int("12a")؟
النص ليس رقماً صحيحاً كاملاً، فيُرجع التحويل nil بدلاً من الانهيار.
الخلاصة
Section titled “الخلاصة”countلعدد الحروف، وuppercased()وlowercased()وreversed()لتحويل النص.prefixوsuffixوdropFirstوdropLastلأخذ أجزاء من الطرفين.- لا وصول بالأرقام: استخدم
String.Indexأو حوّل النص إلىArray. components(separatedBy:)تقسم، وjoined(separator:)تدمج، و"""لعدة أسطر.Int(...)وDouble(...)يُرجعان قيمة اختيارية تفتحها بـif letأو??.