النصوص والمحارف
كيف يفكك البرنامج ما يكتبه المستخدم ويفهمه؟
في نهاية هذه المحاضرة ستكون قادرًا على
- تشرح النص بوصفه سلسلة من المحارف، لكل منها موقع، وتربطه بالمصفوفات
- تستخدم دوال
Stringللبحث والقص والمقارنة والتقسيم - تشرح لماذا لا تتغير النصوص، وتحفظ نتيجة كل دالة في متغير
- تمر على النص بحلقة
forلتعدّ محارفه وتعكسه وتفحصه - تحوّل النص إلى عدد، وتتعامل مع
NumberFormatException - تبني النص داخل الحلقات باستخدام
StringBuilder
محتويات المحاضرة
- معظم ما يكتبه المستخدم نص، وعلى البرنامج أن يفككه
- النص سلسلة محارف مرقّمة من 0، مثل المصفوفة تمامًا
- آخر موقع هو
length() - 1، وما بعده يوقف البرنامج - النص العربي يعمل بالطريقة نفسها، و
lengthتعدّ المحارف - دوال البحث تجيب أين النص الجزئي، وهل هو موجود
- النص لا يتغير أبدًا: كل دالة ترجع نصًا جديدًا
- قارن النصوص بالدوال لا بالعامل
== splitتقطع النص إلى مصفوفة نصوص- حلقة
forتزور كل حرف، كما تزور كل عنصر في المصفوفة - بناء النص المعكوس يقودنا إلى فحص الكلمات المتناظرة
- الصنف
Characterيجيب عن أسئلة كل محرف parseIntوparseDoubleتحولان النص إلى عددStringBuilderيبني النص في مكانه داخل الحلقاتsplitمعparseIntيحولان السطر إلى بيانات: عدّ الكلمات وCSV- فحص كل محرف يصنع شيفرة قيصر ومدقق أسماء المستخدمين
- أسئلة المراجعة
معظم ما يكتبه المستخدم نص، وعلى البرنامج أن يفككه
هل طوله مقبول؟ هل يبدأ بحرف؟ هل فيه رموز ممنوعة؟
يجد الموقع java حتى لو كتبت JAVA وتركت مسافة بعدها.
السطر Sara,80,95 نص واحد، وعلى البرنامج أن يستخرج منه اسمًا وعددين.
النص سلسلة محارف مرقّمة من 0، مثل المصفوفة تمامًا
| العملية | المصفوفة | النص |
|---|---|---|
| العدد | a.length | s.length() |
| عنصر عند موقع | a[i] | s.charAt(i) |
| العنصر الأخير | a[a.length - 1] | s.charAt(s.length() - 1) |
| تغيير عنصر | a[0] = 9; | غير ممكن |
اقرأ النص حرفًا حرفًا
توقّع الأسطر الأربعة قبل التشغيل، ثم ضع اسمك مكان Sara وتوقّع مرة أخرى.
public class Positions {
public static void main(String[] args) {
String name = "Sara";
System.out.println(name.length());
System.out.println(name.charAt(0));
System.out.println(name.charAt(1));
System.out.println(name.charAt(name.length() - 1));
}
}4 S a a
النص ليس مصفوفة في الكتابة
استخدمنا الأقواس المربعة كما في المصفوفة. أصلح السطر ليطبع الحرف الأول.
public class FirstLetter {
public static void main(String[] args) {
String name = "Sara";
char first = name[0];
System.out.println(first);
}
}array required, but String foundأظهر التصحيح
public class FirstLetter {
public static void main(String[] args) {
String name = "Sara";
char first = name.charAt(0);
System.out.println(first);
}
}S
لماذا؟ يشبه النص المصفوفة في الفكرة فقط. الأقواس [] للمصفوفات، أما النص فنطلب منه الحرف بالدالة charAt.
length في النص دالة
public class NameLength {
public static void main(String[] args) {
String name = "Sara";
System.out.println(name.length);
}
}cannot find symbolأظهر التصحيح
public class NameLength {
public static void main(String[] args) {
String name = "Sara";
System.out.println(name.length());
}
}4
لماذا؟ a.length في المصفوفة بلا أقواس، وs.length() في النص بأقواس لأنها دالة. يبحث المترجم عن متغير باسم length داخل String فلا يجده.
آخر موقع هو length() - 1، وما بعده يوقف البرنامج
الخطأ نفسه الذي رأيناه في المصفوفات: النص Sara طوله 4، فمواقعه من 0 إلى 3. الموقع 4 غير موجود.
اقرأ رسالة الخطأ
شغّل البرنامج. يُطبع السطر الأول، ثم يتوقف البرنامج عند الموقع 4. أصلحه بكتابة name.length() - 1.
public class OutOfRange {
public static void main(String[] args) {
String name = "Sara";
System.out.println(name.charAt(3));
System.out.println(name.charAt(name.length()));
System.out.println("This line never runs");
}
}a
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 4النص "Gaza" طوله 4. أي استدعاء يوقف البرنامج؟
النص العربي يعمل بالطريقة نفسها، وlength تعدّ المحارف
تخزن Java كل محرف برقمه في Unicode، فالدوال نفسها تعمل على "سارة" كما تعمل على "Sara"، ويظهر النص صحيحًا على هذا الموقع.
اسم بالعربية
ضع اسمك بالعربية مكان سارة وتوقّع الطول قبل التشغيل.
public class ArabicText {
public static void main(String[] args) {
String name = "سارة";
System.out.println(name);
System.out.println(name.length());
System.out.println(name.charAt(0));
System.out.println(name.contains("ار"));
System.out.println(name + " تتعلم Java");
}
}سارة 4 س true سارة تتعلم Java
تبدو الكلمتان واحدة في القراءة. ماذا يطبع البرنامج؟
public class ArabicLength {
public static void main(String[] args) {
System.out.println("محمد".length());
System.out.println("مُحمد".length());
}
}أظهر الناتج
4 5
لماذا؟ تعدّ length المحارف لا البايتات، ولذلك يبلغ طول محمد أربعة. والضمة محرف مستقل في النص، فتزيد الطول واحدًا.
دوال البحث تجيب أين النص الجزئي، وهل هو موجود
| الدالة | ترجع | s = "Java" |
|---|---|---|
s.substring(i, j) | النص من الموقع i حتى ما قبل j | s.substring(1, 3) → "av" |
s.substring(i) | النص من الموقع i حتى النهاية | s.substring(2) → "va" |
s.indexOf(x) | موقع أول ظهور، أو -1 | s.indexOf("a") → 1 |
s.contains(x) | boolean | s.contains("av") → true |
s.startsWith(x) | boolean | s.startsWith("Ja") → true |
s.endsWith(x) | boolean | s.endsWith("a") → true |
افحص بريدًا إلكترونيًا
عدّ مواقع المحارف حتى @ على الورق، ثم شغّل البرنامج. هل طابقت إجابات الصف في بداية المحاضرة ما تفعله هذه الدوال؟
public class EmailSearch {
public static void main(String[] args) {
String email = "sara.ali@ucas.edu.ps";
int at = email.indexOf("@");
System.out.println(at);
System.out.println(email.substring(0, at));
System.out.println(email.substring(at + 1));
System.out.println(email.contains("ucas"));
System.out.println(email.startsWith("sara"));
System.out.println(email.endsWith(".com"));
System.out.println(email.indexOf("#"));
}
}8 sara.ali ucas.edu.ps true true false -1
ماذا يطبع؟ اكتب مواقع الحروف على الورق أولًا.
public class Cut {
public static void main(String[] args) {
String word = "Programming";
System.out.println(word.substring(3, 7));
}
}أظهر الناتج
gram
لماذا؟ يدخل موقع البداية 3 في النتيجة، ولا يدخل موقع النهاية 7. والطول دائمًا 7 − 3 = 4 محارف.
ماذا ترجع "Java".indexOf("x")؟
-1 ليعني «غير موجود». افحصه قبل استخدام الناتج في substring.اسم المستخدم والنطاق
اقرأ سطرًا فيه بريد إلكتروني. إذا لم يحتوِ على @ فاطبع Invalid. وإلا فاطبع ما قبل @ بعد User: ، ثم ما بعدها بعد Domain: في سطر ثانٍ.
import java.util.Scanner;
public class SplitEmail {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String email = input.nextLine();
// Find the @, then cut the text around it
}
}sara@ucas.edu.ps
User: sara Domain: ucas.edu.ps
تلميح 1
خزّن ناتج indexOf("@") في متغير، وقارنه بالرقم -1.
تلميح 2
substring(0, at) لما قبلها، وsubstring(at + 1) لما بعدها.
الحل
import java.util.Scanner;
public class SplitEmail {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String email = input.nextLine();
int at = email.indexOf("@");
if (at == -1) {
System.out.println("Invalid");
} else {
System.out.println("User: " + email.substring(0, at));
System.out.println("Domain: " + email.substring(at + 1));
}
}
}النص لا يتغير أبدًا: كل دالة ترجع نصًا جديدًا
| الدالة | ترجع | مثال |
|---|---|---|
toUpperCase() | نسخة بأحرف كبيرة | "Ali" → "ALI" |
toLowerCase() | نسخة بأحرف صغيرة | "Ali" → "ali" |
trim() | نسخة بلا مسافات في الطرفين | " a b " → "a b" |
replace(a, b) | نسخة استُبدل فيها كل ظهور | "banana" → "bonono" |
هذا أشهر فخ في النصوص. ماذا يطبع السطران؟
public class Immutable {
public static void main(String[] args) {
String name = "sara";
name.toUpperCase();
System.out.println(name);
name = name.toUpperCase();
System.out.println(name);
}
}أظهر الناتج
sara SARA
لماذا؟ يرجع السطر الثاني نصًا جديدًا فلا يحفظه أحد، ويبقى name كما هو. أما السطر الرابع فيضع النص الجديد في الصندوق، فيتغير المتغير لا النص القديم.
نظّف مدخلات المستخدم
أضفنا القوسين [ ] لتظهر المسافات. لاحظ أن raw في السطر الأخير لم يتغير رغم كل ما سبق. جرّب تبديل ترتيب الدوال في السطر الثالث.
public class CleanText {
public static void main(String[] args) {
String raw = " Hello World ";
System.out.println("[" + raw.trim() + "]");
System.out.println("[" + raw.trim().toLowerCase() + "]");
System.out.println("[" + raw.replace("World", "Java") + "]");
System.out.println("banana".replace("a", "o"));
System.out.println("[" + raw + "]");
}
}[Hello World] [hello world] [ Hello Java ] bonono [ Hello World ]
ماذا يطبع هذا الكود؟
String s = "abc";
s.replace("a", "z");
System.out.println(s);
replace. الحل: s = s.replace("a", "z");.قارن النصوص بالدوال لا بالعامل ==
| الدالة | السؤال | ترجع |
|---|---|---|
a.equals(b) | هل المحارف نفسها تمامًا؟ | boolean |
a.equalsIgnoreCase(b) | هل هي نفسها مع إهمال حالة الأحرف؟ | boolean |
a.compareTo(b) | أيهما يأتي أولًا في ترتيب القاموس؟ | سالب إذا سبق a، و0 إذا تساويا، وموجب إذا تأخر |
ثلاث طرق للمقارنة
تقارن compareTo رقم المحرف في Unicode عند أول اختلاف، والأحرف الكبيرة قبل الصغيرة. ولهذا يأتي Zed قبل apple.
public class CompareStrings {
public static void main(String[] args) {
System.out.println("java".equals("Java"));
System.out.println("java".equalsIgnoreCase("Java"));
System.out.println("apple".compareTo("banana"));
System.out.println("banana".compareTo("apple"));
System.out.println("apple".compareTo("apple"));
System.out.println("Zed".compareTo("apple"));
}
}false true -1 1 0 -7
النصان يحملان المحارف نفسها. ماذا يطبع السطران؟
public class SameText {
public static void main(String[] args) {
String first = "hello";
String part = "hel";
String second = part + "lo";
System.out.println(first == second);
System.out.println(first.equals(second));
}
}أظهر الناتج
false true
لماذا؟ يسأل == هل هما النص نفسه في الذاكرة، وequals هل محارفهما متطابقة. أنشأ الدمج نصًا جديدًا في مكان آخر، فاختلف جواب ==. والنصوص القادمة من المستخدم تقع في الفخ نفسه.
تريد ترتيب الأسماء أبجديًا. ماذا يعني أن a.compareTo(b) أكبر من صفر؟
a يتأخر عن b، فيجب تبديلهما في الترتيب التصاعدي. أما الطول فلا يهم إلا إذا كان أحدهما بداية الآخر.الترتيب بالفقاعات مع النصوص
هذه خوارزمية المحاضرة السابقة، تغيّر فيها شرط المقارنة فقط. اضغط «تتبّع سطرًا سطرًا» وراقب التبديل، ثم جرّب Arrays.sort(names) مكان الحلقتين.
import java.util.Arrays;
public class SortNames {
public static void main(String[] args) {
String[] names = {"Sara", "Ali", "Huda"};
for (int i = 0; i < names.length - 1; i++) {
for (int k = 0; k < names.length - 1 - i; k++) {
if (names[k].compareTo(names[k + 1]) > 0) {
String temp = names[k];
names[k] = names[k + 1];
names[k + 1] = temp;
}
}
}
System.out.println(Arrays.toString(names));
}
}[Ali, Huda, Sara]
split تقطع النص إلى مصفوفة نصوص
هنا يلتقي النص بالمصفوفات: تعطي split فاصلًا، فترجع مصفوفة String[] تمر عليها بكل ما تعلمته في المحاضرتين السابقتين.
من سطر إلى قطع
اضغط «تتبّع سطرًا سطرًا» وانظر إلى المصفوفة parts. غيّر الفاصل إلى ; في النص وفي split.
public class SplitBasics {
public static void main(String[] args) {
String line = "Sara,19,3.75";
String[] parts = line.split(",");
System.out.println(parts.length);
for (int i = 0; i < parts.length; i++) {
System.out.println(i + ": " + parts[i]);
}
}
}3 0: Sara 1: 19 2: 3.75
فاصلتان متتاليتان، وفاصلتان في النهاية. ماذا يطبع؟
public class SplitEmpty {
public static void main(String[] args) {
String[] parts = "one,,three,,".split(",");
System.out.println(parts.length);
System.out.println("[" + parts[1] + "]");
}
}أظهر الناتج
3 []
لماذا؟ تبقى القطعة الفارغة في الوسط، فتكون القطعة الثانية نصًا فارغًا. أما القطع الفارغة في النهاية فتحذفها split، ولذلك يبقى ثلاث قطع.
الفراغات والنقطة
السطر الأول يعطي 7 قطعًا لأن المسافات الزائدة تصنع قطعًا فارغة. والحل في السطر الثاني: trim ثم "\\s+".
public class SplitPatterns {
public static void main(String[] args) {
String text = " one two three ";
System.out.println(text.split(" ").length);
System.out.println(text.trim().split("\\s+").length);
System.out.println("3.75".split(".").length);
System.out.println("3.75".split("\\.").length);
}
}7 3 0 2
حلقة for تزور كل حرف، كما تزور كل عنصر في المصفوفة
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
// use c
}< وليس <=.عدّ حروف العلة
اضغط «تتبّع سطرًا سطرًا» وراقب c وcount. لماذا حوّلنا النص إلى أحرف صغيرة أولًا؟ غيّر النص وتوقّع العدد.
public class CountVowels {
public static void main(String[] args) {
String text = "Programming in Java".toLowerCase();
int count = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
count++;
}
}
System.out.println("Vowels: " + count);
}
}Vowels: 6
ماذا يحدث إذا كتبت الشرط i <= text.length()؟
i الطول، فلا يوجد حرف عند هذا الموقع. وهو خطأ الزيادة بواحد نفسه في المصفوفات.عدّ حروف العلة في سطر
اقرأ سطرًا واطبع عدد حروف العلة a e i o u، سواء كانت كبيرة أو صغيرة. اطبع الرقم فقط.
import java.util.Scanner;
public class VowelCounter {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String text = input.nextLine();
// Count the vowels and print the count
}
}Hello World
3
تلميح 1
حوّل النص كله بالدالة toLowerCase، وخزّن الناتج في متغير.
تلميح 2
قارن char بعلامة ==، فالمحرف نوع بدائي وليس نصًا.
الحل
import java.util.Scanner;
public class VowelCounter {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String text = input.nextLine().toLowerCase();
int count = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
count++;
}
}
System.out.println(count);
}
}بناء النص المعكوس يقودنا إلى فحص الكلمات المتناظرة
اعكس نصًا
نمشي من الموقع الأخير إلى الموقع 0. اضغط «تتبّع سطرًا سطرًا» وراقب reversed يكبر. لاحظ أن كلمة رادار تُقرأ من الطرفين بالطريقة نفسها.
public class ReverseText {
public static String reverse(String text) {
String reversed = "";
for (int i = text.length() - 1; i >= 0; i--) {
reversed = reversed + text.charAt(i);
}
return reversed;
}
public static void main(String[] args) {
System.out.println(reverse("Java"));
System.out.println(reverse("رادار").equals("رادار"));
}
}avaJ true
هنا نمشي إلى الأمام، لكن الحرف يوضع قبل النص. ماذا يطبع؟
public class Prepend {
public static void main(String[] args) {
String word = "abc";
String out = "";
for (int i = 0; i < word.length(); i++) {
out = word.charAt(i) + out;
}
System.out.println(out);
}
}أظهر الناتج
cba
لماذا؟ تأخذ out القيم a ثم ba ثم cba، لأن كل حرف جديد يوضع في المقدمة. وترتيب طرفي + مهم في النصوص.
هل الكلمة متناظرة؟
اقرأ كلمة. اطبع Palindrome إذا كانت تُقرأ من الطرفين بالطريقة نفسها مع إهمال حالة الأحرف، وإلا فاطبع Not palindrome. اكتب دالة isPalindrome ترجع boolean.
import java.util.Scanner;
public class PalindromeCheck {
// Write isPalindrome here
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String word = input.next();
// Call your method and print the answer
}
}level
Palindrome
تلميح 1
اعكس الكلمة كما في البرنامج أعلاه، ثم قارن بالدالة equalsIgnoreCase.
تلميح 2
طريقة أخرى: قارن الموقع i بالموقع length() - 1 - i حتى منتصف الكلمة.
الحل
import java.util.Scanner;
public class PalindromeCheck {
public static boolean isPalindrome(String word) {
String reversed = "";
for (int i = word.length() - 1; i >= 0; i--) {
reversed = reversed + word.charAt(i);
}
return word.equalsIgnoreCase(reversed);
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String word = input.next();
if (isPalindrome(word)) {
System.out.println("Palindrome");
} else {
System.out.println("Not palindrome");
}
}
}الصنف Character يجيب عن أسئلة كل محرف
| الدالة | السؤال أو العمل | مثال |
|---|---|---|
Character.isDigit(c) | هل هو رقم؟ | isDigit('7') → true |
Character.isLetter(c) | هل هو حرف؟ والعربية حروف أيضًا. | isLetter('س') → true |
Character.toUpperCase(c) | يرجع الحرف الكبير | toUpperCase('a') → 'A' |
صنّف المحارف
اضغط «تتبّع سطرًا سطرًا» وتابع العدادات الثلاثة. بدّل النص بآخر فيه أحرف عربية.
public class CharKinds {
public static void main(String[] args) {
String text = "Room 12B!";
int letters = 0;
int digits = 0;
int others = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (Character.isLetter(c)) {
letters++;
} else if (Character.isDigit(c)) {
digits++;
} else {
others++;
}
}
System.out.println("Letters: " + letters);
System.out.println("Digits: " + digits);
System.out.println("Others: " + others);
}
}Letters: 5 Digits: 2 Others: 2
المحرف في الحقيقة رقم. ماذا يطبع؟ رقم a في Unicode هو 97.
public class CharMath {
public static void main(String[] args) {
char c = 'a';
System.out.println(c + 1);
System.out.println((char) (c + 1));
System.out.println(Character.toUpperCase(c) + "bc");
}
}أظهر الناتج
98 b Abc
لماذا؟ char + int يعطي int، فيطبع 98. والتحويل (char) يعيد الرقم إلى حرف. وفي السطر الأخير طرف نصي، فيكون + دمجًا. سنحتاج إلى هذه الفكرة في شيفرة قيصر.
محرف واحد أم نص؟
public class Grade {
public static void main(String[] args) {
char grade = "A";
System.out.println(grade);
}
}incompatible types: String cannot be converted to charأظهر التصحيح
public class Grade {
public static void main(String[] args) {
char grade = 'A';
System.out.println(grade);
}
}A
لماذا؟ العلامتان " " تصنعان نصًا حتى لو كان حرفًا واحدًا. والمحرف يُكتب بين ' '.
parseInt وparseDouble تحولان النص إلى عدد
القطع التي تخرج من split نصوص، حتى لو بدت أرقامًا. قبل أي حساب يجب تحويلها.
من نص إلى عدد
public class ParseNumbers {
public static void main(String[] args) {
String age = "19";
System.out.println(age + 1);
int years = Integer.parseInt(age);
System.out.println(years + 1);
double gpa = Double.parseDouble("3.75");
System.out.println(gpa * 2);
}
}191 20 7.5
ماذا يطبع السطران؟
public class ParseOrJoin {
public static void main(String[] args) {
String a = "5";
String b = "3";
System.out.println(a + b);
System.out.println(Integer.parseInt(a) + Integer.parseInt(b));
}
}أظهر الناتج
53 8
لماذا؟ بين نصين تعني + الدمج. وبعد التحويل يصبح الطرفان عددين، فتعني الجمع.
نص ليس عددًا
شغّله واقرأ الرسالة: For input string: "abc". يتوقف البرنامج عند السطر الذي فيه parseInt.
public class ParseCrash {
public static void main(String[] args) {
System.out.println("Before");
int n = Integer.parseInt("abc");
System.out.println(n);
}
}Before
لمحة: try وcatch يلتقطان الاستثناء
إذا ظهر الاستثناء داخل try انتقل التنفيذ إلى catch بدل أن يتوقف البرنامج. هذه لمحة فقط تكفي لهذا الاستثناء، وستدرسها بعمق في برمجة 2. لاحظ أن " 7" يفشل بسبب المسافة، فاستخدم trim قبل التحويل.
public class ParseTry {
public static void main(String[] args) {
String[] inputs = {"42", "3.5", "abc", " 7", ""};
for (String text : inputs) {
try {
int n = Integer.parseInt(text);
System.out.println("OK: " + n);
} catch (NumberFormatException e) {
System.out.println(e.getMessage());
}
}
}
}OK: 42 For input string: "3.5" For input string: "abc" For input string: " 7" For input string: ""
ماذا يحدث عند تنفيذ Integer.parseInt("3.5")؟
parseInt لا تقرّب ولا تحذف الكسر: النص 3.5 ليس عددًا صحيحًا. والمترجم لا يفحص محتوى النص، فيظهر الخطأ أثناء التشغيل فقط. استخدم parseDouble لهذا النص.StringBuilder يبني النص في مكانه داخل الحلقات
بما أن النص لا يتغير، فإن كل s = s + c في حلقة تنشئ نصًا جديدًا وتنسخ كل ما سبق. لا يهم ذلك مع كلمة قصيرة، لكنه بطيء مع نص طويل. أما StringBuilder فصندوق نص قابل للتعديل، ينمو دون نسخ كل مرة.
| الدالة | تعمل |
|---|---|
new StringBuilder() | صندوق فارغ، مثلما تنشئ Scanner بالكلمة new |
sb.append(x) | تضيف نصًا أو حرفًا أو عددًا في النهاية |
sb.reverse() | تعكس المحتوى في مكانه |
sb.length() | عدد المحارف حتى الآن |
sb.toString() | ترجع String عاديًا، لاستخدام دواله |
ابنِ سطرًا في حلقة
اضغط «تتبّع سطرًا سطرًا» وراقب line يكبر. لماذا لا تظهر فاصلة بعد الرقم الأخير؟
public class BuilderLine {
public static void main(String[] args) {
StringBuilder line = new StringBuilder();
for (int i = 1; i <= 5; i++) {
line.append(i);
if (i < 5) {
line.append(", ");
}
}
String result = line.toString();
System.out.println(result);
System.out.println(result.length());
}
}1, 2, 3, 4, 5 13
لا يحفظ هذا البرنامج ما ترجعه أي دالة. ماذا يطبع؟
public class BuilderVsString {
public static void main(String[] args) {
StringBuilder box = new StringBuilder("abc");
box.append("d");
System.out.println(box);
String text = "abc";
text.concat("d");
System.out.println(text);
box.reverse();
System.out.println(box);
}
}أظهر الناتج
abcd abc dcba
لماذا؟ StringBuilder يتغير هو نفسه، فلا حاجة إلى حفظ الناتج. أما concat فهي مثل +، ترجع نصًا جديدًا يضيع هنا.
احذف حروف العلة
اقرأ سطرًا واطبعه دون حروف العلة الصغيرة والكبيرة a e i o u، مع إبقاء كل ما سواها. استخدم StringBuilder.
import java.util.Scanner;
public class RemoveVowels {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String text = input.nextLine();
StringBuilder result = new StringBuilder();
// Append every character that is not a vowel
System.out.println(result);
}
}Programming
Prgrmmng
تلميح 1
حوّل كل محرف إلى صغير بالدالة Character.toLowerCase لتقارنه مرة واحدة، لكن أضف المحرف الأصلي.
الحل
import java.util.Scanner;
public class RemoveVowels {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String text = input.nextLine();
StringBuilder result = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
char low = Character.toLowerCase(c);
if (low != 'a' && low != 'e' && low != 'i' && low != 'o' && low != 'u') {
result.append(c);
}
}
System.out.println(result);
}
}split مع parseInt يحولان السطر إلى بيانات: عدّ الكلمات وCSV
عدّ الكلمات
اقرأ سطرًا واطبع عدد الكلمات فيه. قد توجد مسافات زائدة في أي مكان، وقد يكون السطر فارغًا فيكون الجواب 0.
import java.util.Scanner;
public class WordCount {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String line = input.nextLine();
// Clean the line, split it, and print the number of pieces
}
}Java is fun
3
تلميح 1
استخدم trim ثم split("\\s+").
تلميح 2
"".split(...) ترجع مصفوفة فيها قطعة واحدة فارغة، فافحص السطر الفارغ بعد trim قبل التقسيم.
الحل
import java.util.Scanner;
public class WordCount {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String line = input.nextLine().trim();
if (line.length() == 0) {
System.out.println(0);
} else {
System.out.println(line.split("\\s+").length);
}
}
}قارئ أسطر CSV
CSV هو تنسيق جداول Excel حين تُحفظ نصًا: كل سطر صف، والفاصلة تفصل الأعمدة. اضغط «تتبّع سطرًا سطرًا» وانظر إلى parts. أضف سطرًا لطالب جديد إلى المصفوفة.
public class CsvParser {
public static void main(String[] args) {
String[] lines = {"Sara Ali, 19, 3.75", "Omar, 20, 2.5"};
for (String line : lines) {
String[] parts = line.split(",");
String name = parts[0].trim();
int age = Integer.parseInt(parts[1].trim());
double gpa = Double.parseDouble(parts[2].trim());
System.out.printf("%-10s %3d %5.2f%n", name, age + 1, gpa);
}
}
}Sara Ali 20 3.75 Omar 21 2.50
سطر درجات
اقرأ سطرًا بالشكل name,mark1,mark2,mark3، وقد توجد مسافات حول الفواصل. اطبع Name: مع الاسم، ثم Total: مع المجموع، ثم Average: مع المعدل بمنزلتين. وإذا لم يكن في السطر أربع قطع بالضبط، فاطبع Bad line فقط.
import java.util.Scanner;
public class CsvLine {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String line = input.nextLine();
// Split, check the number of pieces, convert, print
}
}Sara,80,90,100
Name: Sara Total: 270 Average: 90.00
تلميح 1
استدعِ trim على كل قطعة قبل parseInt.
تلميح 2
احذر قسمة الأعداد الصحيحة: total / 3.0.
الحل
import java.util.Scanner;
public class CsvLine {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String line = input.nextLine();
String[] parts = line.split(",");
if (parts.length != 4) {
System.out.println("Bad line");
} else {
int total = 0;
for (int i = 1; i < parts.length; i++) {
total += Integer.parseInt(parts[i].trim());
}
System.out.println("Name: " + parts[0].trim());
System.out.println("Total: " + total);
System.out.printf("Average: %.2f%n", total / 3.0);
}
}
}فحص كل محرف يصنع شيفرة قيصر ومدقق أسماء المستخدمين
شيفرة قيصر للأحرف الصغيرة
c - 'a' يعطي رقم الحرف من 0 إلى 25، و% 26 يعيدنا إلى البداية بعد z. اضغط «تتبّع سطرًا سطرًا» وتوقف عند الحرف z لترى ذلك. لفك الشيفرة نزيح بالمقدار 26 - key.
public class CaesarLab {
public static String encrypt(String text, int key) {
StringBuilder out = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c >= 'a' && c <= 'z') {
out.append((char) ('a' + (c - 'a' + key) % 26));
} else {
out.append(c);
}
}
return out.toString();
}
public static void main(String[] args) {
String secret = encrypt("lazy zebra", 3);
System.out.println(secret);
System.out.println(encrypt(secret, 26 - 3));
}
}odcb cheud lazy zebra
شيفرة قيصر كاملة
اقرأ المفتاح في السطر الأول (من 0 إلى 25) والرسالة في السطر الثاني. اطبع الرسالة مشفرة: تُزاح الأحرف الصغيرة والكبيرة وتبقى على حالتها، ولا يتغير أي محرف آخر.
import java.util.Scanner;
public class Caesar {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int key = input.nextInt();
// Careful: what does nextInt leave behind?
}
}3 abc xyz
def abc
تلميح 1
بعد nextInt استدعِ nextLine مرة لتجاوز نهاية السطر الأول، ثم مرة ثانية لقراءة الرسالة.
تلميح 2
أضف فرعًا ثانيًا للأحرف الكبيرة يبدأ من 'A'.
الحل
import java.util.Scanner;
public class Caesar {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int key = input.nextInt();
input.nextLine();
String text = input.nextLine();
StringBuilder out = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c >= 'a' && c <= 'z') {
out.append((char) ('a' + (c - 'a' + key) % 26));
} else if (c >= 'A' && c <= 'Z') {
out.append((char) ('A' + (c - 'A' + key) % 26));
} else {
out.append(c);
}
}
System.out.println(out);
}
}مدقق اسم المستخدم
اقرأ سطرًا وافحصه بهذا الترتيب، واطبع أول خطأ فقط: طوله من 5 إلى 12 وإلا Bad length، ويبدأ بحرف وإلا Bad start، وكل محارفه حروف أو أرقام أو _ وإلا Bad character. وإذا نجحت كل الفحوص فاطبع Valid. اكتب دالة ترجع الرسالة.
import java.util.Scanner;
public class UsernameValidator {
public static String check(String name) {
// Return the first problem, or "Valid"
return "Valid";
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String name = input.nextLine();
System.out.println(check(name));
}
}sara_99
Valid
تلميح 1
افحص الطول أولًا، فهو يحميك من charAt(0) على نص فارغ.
تلميح 2
return توقف الدالة فورًا، فلا تحتاج إلى else بعد كل فحص.
الحل
import java.util.Scanner;
public class UsernameValidator {
public static String check(String name) {
if (name.length() < 5 || name.length() > 12) {
return "Bad length";
}
if (!Character.isLetter(name.charAt(0))) {
return "Bad start";
}
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (!Character.isLetter(c) && !Character.isDigit(c) && c != '_') {
return "Bad character";
}
}
return "Valid";
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String name = input.nextLine();
System.out.println(check(name));
}
}أسئلة المراجعة
أي كتابة صحيحة لطول النص s ولطول المصفوفة a؟
cannot find symbol.أي شرط صحيح لفحص كلمة المرور pass المقروءة من المستخدم؟
== تقارن المكان في الذاكرة، والنص المقروء في مكان جديد. و= إسناد وليست مقارنة، فلا يصلح شرطًا.متى تختار StringBuilder بدل +؟
+ في حلقة تنسخ كل ما سبق. وStringBuilder ينمو في مكانه.عدة دوال في برنامج واحد. ماذا يطبع؟
public class ReviewStrings {
public static void main(String[] args) {
String line = " Ali,Sara ";
String[] names = line.trim().split(",");
System.out.println(names[1].toUpperCase().charAt(0));
System.out.println(names[0].length() + names[1].length());
System.out.println(line.length());
}
}أظهر الناتج
S 7 10
لماذا؟ names[1] هو Sara، وأول حرف كبير منه S. والطولان 3 و4 عددان فيجمعان 7. أما line فلم يتغير رغم trim، فما زال طوله 10.