تخطَّ إلى المحتوى

النصوص في Kotlin: String وأهم دوالها

الدرس 8 من 11 · Kotlin

رسالة ترحيب، واسم مستخدم، ونص إشعار: تطبيقات أندرويد تتعامل مع النصوص في كل شاشة. تعرّفت على String وعلى قوالب النصوص بعلامة $ في درس المتغيرات. في هذا الدرس ستتعلم أهم دوال النصوص في Kotlin: كيف تقطع النص وتبحث فيه وتنظفه وتحوّله إلى أرقام بأمان.

النص سلسلة حروف، ولكل حرف رقم يبدأ من صفر:

val word = "Kotlin"
println(word.length)
println(word[0])
println(word.first())
println(word.last())
println(word.reversed())
الناتج
6
K
K
n
niltoK
  • length: عدد الحروف، وهي خاصية بدون أقواس.
  • word[0]: الحرف الأول، بنفس طريقة القوائم.
  • first() و last(): أوضح في القراءة من حساب رقم آخر حرف.

تخيّل النص مثل قطار: كل عربة فيها حرف واحد، والعربات مرقمة من المقدمة.

يمكنك أيضاً المرور على الحروف بحلقة for:

var vowels = 0
for (ch in "education") {
if (ch in "aeiou") vowels++
}
println(vowels)
الناتج
5

النص في Kotlin غير قابل للتعديل (Immutable). أي دالة «تغيّر» النص تُرجع نصاً جديداً، والأصلي يبقى كما هو:

val name = "sara"
name.uppercase()
println(name)
val upper = name.uppercase()
println(upper)
الناتج
sara
SARA

السطر الثاني بلا فائدة، لأن النتيجة لم تُخزَّن في متغير.

val text = " Hello Kotlin "
val clean = text.trim()
println("[$clean]")
println(clean.substring(6))
println(clean.substring(0, 5))
println(clean.take(3))
println(clean.takeLast(3))
println(clean.lowercase())
println(clean.replace("Kotlin", "World"))
الناتج
[Hello Kotlin]
Kotlin
Hello
Hel
lin
hello kotlin
Hello World
  • trim(): تحذف المسافات من الطرفين. استخدمها دائماً مع ما يكتبه المستخدم.
  • substring(0, 5): من الموضع 0 إلى ما قبل الموضع 5.
  • take(n) و takeLast(n): أول أو آخر n حروف، ولا تسببان خطأ إذا كان النص أقصر.
val email = "omar@example.com"
println(email.contains("@"))
println("@" in email)
println(email.indexOf("@"))
println(email.startsWith("omar"))
println(email.endsWith(".org"))
println(email.substringBefore("@"))
println(email.substringAfter("@"))
الناتج
true
true
4
true
false
omar
example.com

الكلمة in طريقة مختصرة لكتابة contains. والدالتان substringBefore و substringAfter من هدايا Kotlin: تستخرجان ما قبل الفاصل أو ما بعده دون حساب أي مواضع.

هنا تختلف Kotlin عن Java اختلافاً مريحاً: العلامة == تقارن محتوى النصين مباشرة:

val a = "kotlin"
val b = "KOTLIN".lowercase()
println(a == b)
println(a.equals("Kotlin", ignoreCase = true))
println("apple" < "banana")
println("".isEmpty())
println(" ".isBlank())
الناتج
true
true
true
true
true
  • ==: هل المحتوى متطابق؟ (في Java كانت تحتاج equals).
  • ignoreCase = true: مقارنة تتجاهل حالة الحروف.
  • < و >: مقارنة أبجدية.
  • isEmpty(): النص بلا أي حرف. و isBlank(): النص فارغ أو كله مسافات، وهي الأنسب لفحص حقول الإدخال.
val line = "تفاح,موز,عنب"
val fruits = line.split(",")
println(fruits)
println(fruits.size)
println(fruits.joinToString(" - "))
الناتج
[تفاح, موز, عنب]
3
تفاح - موز - عنب

split تقطع النص عند الفاصل وتُرجع قائمة List، و joinToString تفعل العكس.

لكتابة نص طويل على عدة أسطر، استخدم ثلاث علامات تنصيص:

val name = "ليلى"
val message = """
مرحباً $name،
تم تسجيلك بنجاح.
رقم طلبك: ${100 + 23}
""".trimIndent()
println(message)
الناتج
مرحباً ليلى،
تم تسجيلك بنجاح.
رقم طلبك: 123

trimIndent() تحذف الإزاحة المشتركة من أول كل سطر، فيبقى كودك مرتباً والنص نظيفاً. والقوالب $name و ${...} تعمل داخلها كالمعتاد.

ما يكتبه المستخدم يصلك نصاً دائماً. الدالة toInt() تحوّله إلى عدد، لكنها ترمي خطأ إذا لم يكن رقماً. البديل الآمن هو toIntOrNull():

val inputs = listOf("42", "abc", " 7 ")
for (input in inputs) {
val number = input.trim().toIntOrNull()
if (number != null) {
println("العدد: $number وضعفه ${number * 2}")
} else {
println("«$input» ليس رقماً")
}
}
الناتج
العدد: 42 وضعفه 84
«abc» ليس رقماً
العدد: 7 وضعفه 14

buildString: بناء نص داخل حلقة

Section titled “buildString: بناء نص داخل حلقة”

لأن النص لا يتغير، فكل + داخل حلقة ينشئ نصاً جديداً كاملاً. الدالة buildString تبني النص بكفاءة:

val stars = buildString {
for (i in 1..3) {
append("*".repeat(i))
append("\n")
}
}
print(stars)
println("=".repeat(10))
println("7".padStart(3, '0'))
الناتج
*
**
***
==========
007
  • append: تضيف إلى النص الجاري بناؤه.
  • repeat(n): تكرر النص n مرات.
  • padStart(3, '0'): تكمل النص من البداية حتى يصل طوله إلى 3.
  1. خزّن جملة في متغير، واطبع عدد حروفها وعدد كلماتها باستخدام split(" ").
  2. خزّن اسم ملف مثل photo.png، واستخرج الاسم والامتداد بـ substringBefore و substringAfter.
  3. اكتب دالة initials(fullName: String) تُرجع الحرف الأول من كل كلمة: "Omar Ali" تصبح "OA".
  4. تحدٍّ: اكتب دالة isPalindrome(word: String) تُرجع true إذا كانت الكلمة تُقرأ من الجهتين بنفس الشكل مثل level، مع تجاهل حالة الحروف.
  • تجاهل القيمة المُرجعة: text.trim() وحدها لا تغيّر text. خزّن النتيجة: val clean = text.trim().
  • موضع خارج الحدود: "Kotlin"[6] يرمي StringIndexOutOfBoundsException، فآخر موضع هو 5. الدالة getOrNull(6) تُرجع null بدلاً من الخطأ.
  • toInt مع إدخال المستخدم: "abc".toInt() يرمي NumberFormatException. استخدم toIntOrNull().
  • نسيان الأقواس في القالب: "$user.name" تطبع الكائن ثم .name حرفياً. الصحيح "${user.name}".

سؤال

ما ناتج "Hello".substring(1, 3)؟

سؤال

ما ناتج "12a".toIntOrNull()؟

  • النص سلسلة حروف مرقمة من صفر: length و [i] و first() و last().
  • النصوص لا تتغير: كل دالة تُرجع نصاً جديداً يجب أن تخزنه.
  • == تقارن المحتوى مباشرة، و isBlank() تفحص الحقول الفارغة.
  • split و joinToString للتقسيم والدمج، و """...""" للنصوص متعددة الأسطر.
  • toIntOrNull() للتحويل الآمن، و buildString لبناء النصوص داخل الحلقات.