النصوص في Java: String وأهم دوالها
اسم المستخدم، ورسالة الخطأ، وعنوان المقال: معظم ما يعرضه أي برنامج نصوص. استخدمت String من أول درس، لكنها تخفي عشرات الدوال المفيدة. في هذا الدرس ستتعلم كيف تقيس النص وتقطعه وتبحث فيه وتقارنه بالطريقة الصحيحة، ولماذا نحتاج StringBuilder أحياناً.
طول النص وحروفه
Section titled “طول النص وحروفه”النص سلسلة من الحروف، ولكل حرف رقم يبدأ من صفر، تماماً مثل المصفوفات:
String word = "Java";System.out.println(word.length());System.out.println(word.charAt(0));System.out.println(word.charAt(word.length() - 1));System.out.println(word.isEmpty());4Jafalselength(): عدد الحروف. لاحظ أنها دالة بأقواس هنا، بعكسlengthفي المصفوفات.charAt(i): الحرف في الموضعi، ونوعهchar.isEmpty(): هل النص فارغ تماماً""؟
تخيّل النص مثل عقد من الخرز: كل خرزة حرف، ولكل خرزة مكان مرقّم في الخيط.
النصوص لا تتغير
Section titled “النصوص لا تتغير”هذه أهم معلومة في الدرس: النص في Java غير قابل للتعديل (Immutable). كل دالة «تغيّر» النص تُرجع في الحقيقة نصاً جديداً وتترك الأصلي كما هو:
String name = "sara";name.toUpperCase();System.out.println(name);
String upper = name.toUpperCase();System.out.println(upper);saraSARAالسطر الثاني لم يفعل شيئاً مفيداً، لأننا لم نخزن النص الجديد. تذكّر دائماً أن تستقبل النتيجة في متغير.
التقطيع والتنظيف
Section titled “التقطيع والتنظيف”String text = " Hello Java ";String clean = text.trim();System.out.println("[" + clean + "]");System.out.println(clean.substring(6));System.out.println(clean.substring(0, 5));System.out.println(clean.toLowerCase());System.out.println(clean.replace("Java", "World"));[Hello Java]JavaHellohello javaHello Worldtrim(): تحذف المسافات من البداية والنهاية. مفيدة جداً مع ما يكتبه المستخدم.substring(6): من الموضع 6 إلى النهاية.substring(0, 5): من الموضع 0 إلى ما قبل الموضع 5.replace(a, b): تستبدل كل ظهور للنص الأول بالثاني.
البحث داخل النص
Section titled “البحث داخل النص”String email = "omar@example.com";System.out.println(email.contains("@"));System.out.println(email.indexOf("@"));System.out.println(email.indexOf("#"));System.out.println(email.startsWith("omar"));System.out.println(email.endsWith(".org"));true4-1truefalseindexOf تُرجع موضع أول ظهور، وتُرجع -1 إذا لم تجد شيئاً. يمكنك دمجها مع substring لاستخراج جزء من النص:
String email = "omar@example.com";int at = email.indexOf("@");String user = email.substring(0, at);String domain = email.substring(at + 1);System.out.println(user);System.out.println(domain);omarexample.comالمقارنة: equals وليس ==
Section titled “المقارنة: equals وليس ==”في درس الشروط عرفت أن مقارنة النصوص تكون بـ equals. لنرَ السبب عملياً:
String a = "java";String b = new String("java");System.out.println(a == b);System.out.println(a.equals(b));System.out.println(a.equalsIgnoreCase("JAVA"));System.out.println("apple".compareTo("banana") < 0);falsetruetruetrue==تسأل: هل المتغيران يشيران إلى نفس الكائن في الذاكرة؟equalsتسأل: هل المحتوى متطابق؟ وهذا ما نريده دائماً تقريباً.equalsIgnoreCase: مقارنة تتجاهل حالة الحروف.compareTo: مقارنة أبجدية؛ تُرجع عدداً سالباً إذا كان النص الأول يأتي قبل الثاني، وموجباً إذا كان بعده، وصفراً عند التساوي.
التقسيم والدمج
Section titled “التقسيم والدمج”String line = "تفاح,موز,عنب";String[] fruits = line.split(",");System.out.println(fruits.length);System.out.println(fruits[1]);
String joined = String.join(" - ", fruits);System.out.println(joined);3موزتفاح - موز - عنبsplit تقطع النص عند فاصل معيّن وتُرجع مصفوفة، و String.join تفعل العكس. ستحتاجهما كثيراً عند قراءة بيانات من ملف أو من المستخدم.
تنسيق النصوص
Section titled “تنسيق النصوص”بدلاً من سلسلة طويلة من علامات +، استخدم String.format:
String name = "ليلى";int age = 14;double avg = 91.456;String msg = String.format("%s عمرها %d ومعدلها %.1f", name, age, avg);System.out.println(msg);ليلى عمرها 14 ومعدلها 91.5%s مكان لنص، و %d لعدد صحيح، و %.1f لعدد عشري برقم واحد بعد الفاصلة.
StringBuilder: بناء النصوص بكفاءة
Section titled “StringBuilder: بناء النصوص بكفاءة”لأن النص لا يتغير، فكل عملية + داخل حلقة تنشئ نصاً جديداً كاملاً وترمي القديم. مع آلاف التكرارات يصبح ذلك بطيئاً. الحل هو StringBuilder: نص قابل للتعديل تضيف إليه ثم تحوّله إلى String في النهاية:
StringBuilder sb = new StringBuilder();for (int i = 1; i <= 5; i++) { sb.append(i); sb.append(" ");}String result = sb.toString().trim();System.out.println(result);System.out.println(new StringBuilder("abc").reverse());1 2 3 4 5cbaالفرق مثل الكتابة بقلم حبر على ورقة جديدة في كل مرة، مقابل الكتابة على سبورة تضيف إليها وتمسح منها كما تشاء.
جرّب بنفسك
Section titled “جرّب بنفسك”- اكتب برنامجاً يخزن جملة في متغير، ثم يطبع عدد حروفها وعدد كلماتها (استخدم
split(" ")). - اطبع الجملة معكوسة باستخدام
StringBuilder. - خزّن اسم ملف مثل
photo.png، واستخرج الامتداد باستخدامlastIndexOf(".")وsubstring. - تحدٍّ: اكتب دالة
isPalindromeتستقبل كلمة وتُرجعtrueإذا كانت تُقرأ من الجهتين بنفس الشكل مثلlevel.
أخطاء شائعة
Section titled “أخطاء شائعة”- مقارنة النصوص بـ ==: استخدم
equalsدائماً. هذا الخطأ لا يظهر عند الترجمة، بل يعطي نتائج خاطئة وقت التشغيل. - تجاهل القيمة المُرجعة:
text.trim();وحدها لا تغيّرtext. اكتبtext = text.trim();. - موضع خارج الحدود:
"Java".charAt(4)يسبب الخطأStringIndexOutOfBoundsException، لأن آخر موضع هو 3.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما ناتج "Hello".substring(1, 3)؟
التقطيع يبدأ من الموضع 1 وينتهي قبل الموضع 3، فيأخذ الحرفين e و l فقط.
سؤال
لماذا نستخدم equals بدلاً من == لمقارنة نصين؟
== تقارن المراجع، أما equals فتقارن حروف النصين نفسها.
الخلاصة
Section titled “الخلاصة”- النص سلسلة حروف مرقمة من صفر:
length()وcharAt()وsubstring(). - النصوص لا تتغير: كل دالة تُرجع نصاً جديداً يجب أن تخزنه.
- قارن المحتوى بـ
equalsأوequalsIgnoreCase، ولا تستخدم==. splitتقسم النص إلى مصفوفة، وString.joinتدمج، وString.formatتنسّق.- لبناء نص داخل حلقة استخدم
StringBuilderثمtoString().