مقدمة الإدخال والإخراج + طباعة بأناقة
حتى الآن كانت برامجنا تتحدث وحدها — تطبع على الشاشة ولا تسمع أحدًا. هذه الوحدة تعلّمها الاستماع.
كل برنامج له قناتان أساسيتان مع العالم الخارجي: الإخراج (Output) — ما يظهر على الشاشة، وقد استخدمناه منذ الدرس الأول عبر System.out.print وprintln (وفرّق بينهما في جدول المقارنة في درس 1·2)، والإدخال (Input) — ما يكتبه المستخدم أثناء التشغيل، وسنفتحه في الدرس التالي.
قبل أن نفتح باب الإدخال، لنتقن الإخراج أولًا. أحيانًا نريد طباعة نص يحتوي سطرًا جديدًا أو علامة تنصيص داخل النص نفسه — هنا تظهر رموز الهروب (Escape Characters): شرطة مائلة خلفية تليها حرف واحد، يفهمها جافا كأمر تنسيق:
| الرمز | اسمه | ماذا يفعل |
|---|---|---|
| \n | سطر جديد (newline) | ينزل إلى بداية السطر التالي — مثل println من داخل النص |
| \t | جدولة (tab) | مسافة بادئة تُنظم الأعمدة في الطباعة |
| \" | علامة تنصيص | يطبع " بدون أن يظن جافا أن النص انتهى |
| \\ | شرطة مائلة | يطبع \ نفسها — لأن كتابة \ وحدها تُفسَّر بداية رمز هروب |
public class EscapeChars { public static void main(String[] args) { System.out.println("سطر أول\nسطر ثانٍ"); System.out.println("عمود1\tعمود2\tعمود3"); System.out.println("مسار الملف: C:\\Users\\Sara"); System.out.println("قال المعلم: \"جافا ممتعة\""); }}
اقرأ المثال سطرًا سطرًا
- النص الأول يحمل \n في منتصفه — لاحظ أن المخرجات تنقسم على سطرين رغم أننا استخدمنا println مرة واحدة.
- \t تترك مسافة جدولة واسعة بين «عمود1» و«عمود2» — أساس تنسيق الجداول النصية قبل أن نتعرف على printf.
- في مسار الملف كتبنا \\ لأن \U وحدها ليست رمز هروب صالحًا — جافا الحقيقية ترفضها بخطأ ترجمة (illegal escape character)، لذلك يجب مضاعفة الشرطة المائلة دائمًا عند طباعتها.
- \" تسمح بعلامتي تنصيص داخل النص — تذكّر أنها بديل الدرس 3·1 الذي شرحنا فيه \" بالفعل، والآن تعرف اسمها: رمز هروب.
System.out.print("A");
System.out.println("B");
System.out.print("C");كلاس Scanner وقراءة المدخلات
الآن نفتح باب الإدخال: برنامج يسأل المستخدم سؤالًا ويتوقف منتظرًا إجابته، ثم يكمل التنفيذ بناءً عليها.
تخيّل موظف استقبال في مكتبة: يقف عند الباب، يستقبل كل قادم ويسجّل ما يقوله في الاستمارة. في جافا هذا الموظف هو Scanner — كلاس جاهز يقرأ ما يكتبه المستخدم على لوحة المفاتيح. ثلاث خطوات ثابتة في كل برنامج:
- في أول السطر الأول: import java.util.Scanner; — أمر للمترجم أن يجلب كلاس Scanner من مكتبة جافا الجاهزة.
- داخل main ننشئ الموظف: Scanner sc = new Scanner(System.in); — حيث System.in تعني «اقرأ من لوحة المفاتيح».
- ثم نستدعيه متى شئنا: sc.nextInt() وغيرها من ميثودات القراءة.
اكشف الإجابة
input.nextLine() — نناديه باسمه الجديد input؛ فالاسم يختاره المبرمج، أما الموظف وميثوداته فهي نفسها.| الميثود | تقرأ ماذا؟ | تُخزَّن في |
|---|---|---|
| sc.nextInt() | رقمًا صحيحًا (int) | int age = sc.nextInt(); |
| sc.nextDouble() | رقمًا عشريًا (double) | double price = sc.nextDouble(); |
| sc.nextLine() | سطرًا كاملًا حتى نهايته | String name = sc.nextLine(); |
| sc.next() | كلمة واحدة فقط (حتى أول مسافة) | String word = sc.next(); |
| sc.nextBoolean() | true أو false — في جافا الكاملة | (غير متاحة في بيئة الورشة) |
أول برنامج: يقرأ رقمين ويطبع مجموعهما — لاحظ أن كل ميثود قراءة تتوقف وتنتظر إجابتك:
import java.util.Scanner;public class SumTwo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("أدخل الرقم الأول: "); int a = sc.nextInt(); System.out.print("أدخل الرقم الثاني: "); int b = sc.nextInt(); System.out.println("المجموع: " + (a + b)); }}
وماذا لو كتب المستخدم أكثر من قيمة في سطر واحد؟ ميثودات القراءة الرقمية والكلمة تتجاهل المسافات وتقرأ الرموز واحدًا واحدًا — جرّب إدخال «7 5» في السؤال الأول وسترى البرنامج يقرأهما معًا:
import java.util.Scanner;public class TwoOnOneLine { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("أدخل رقمين على سطر واحد:"); int a = sc.nextInt(); int b = sc.nextInt(); System.out.println("قرأت " + a + " و " + b + " — المجموع " + (a + b)); }}
ولقراءة نص وعدد من نفس السطر، وقراءة سعر عشري مع كمية:
import java.util.Scanner;public class GreetUser { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("اكتب اسمك وعمرك مفصولين بمسافة: "); String name = sc.next(); int age = sc.nextInt(); System.out.println("أهلًا " + name + "، عمرك " + age); }}
import java.util.Scanner;public class DoubleInput { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("أدخل سعر القطعة: "); double price = sc.nextDouble(); System.out.print("كم قطعة تريد؟ "); int qty = sc.nextInt(); System.out.println("الإجمالي: " + (price * qty)); }}
int a = sc.nextInt(); int b = sc.nextInt(); System.out.println(a + b);
فخ nextLine() الشهير + تطبيقات عملية
أشهر فخ يقع فيه كل مبرمج جافا مبتدئ — اسمه «السطر المتبقي». سيصيبك يومًا ما، لكنك الآن ستعرفه من قبل أن يقع.
تذكر قاعدة الدرس السابق: ميثودات مثل nextInt() تقرأ الرمز (الرقم) من السطر وتتوقف، ولا تستهلك بقية السطر. أما nextLine() فتقرأ كل ما تبقى حتى نهاية السطر — حتى لو كان هذا الباقي فارغًا تمامًا! شغّل هذا المثال وأدخل «17» ثم «سارة المهداوي» وراقب الكارثة الصامتة:
import java.util.Scanner;public class NameTrap { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("كم عمرك؟ "); int age = sc.nextInt(); System.out.print("ما اسمك الكامل؟ "); String name = sc.nextLine(); // ← الفخ: تقرأ بقية سطر العمر الفارغ! System.out.println("عمرك: " + age); System.out.println("اسمك: [" + name + "]"); }}
النتيجة؟ اسمك: [] — الاسم فارغ! ما حدث بالضبط: عندما كتبت «17» وضغطت Enter، بقي في ذاكرة Scanner شيء واحد: نهاية ذلك السطر الفارغة. سأل البرنامج عن الاسم فالتقطت nextLine() هذه النهاية الفارغة، ولم يتبقَّ شيء لقراءة «سارة المهداوي».
توقّع: أين سيقف المؤشر بعد nextInt()؟
لماذا لم يقرأ Sara؟
الحل
قبل أي nextLine() تأتي بعد قراءة رمز، أدخل استدعاءً إضافيًا «لتفريغ» بقية السطر — كأنك ترمي الفراغ في سلة المهملات:
import java.util.Scanner;public class NameFixed { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("كم عمرك؟ "); int age = sc.nextInt(); sc.nextLine(); // ← استهلك بقية سطر العمر (الفراغ) System.out.print("ما اسمك الكامل؟ "); String name = sc.nextLine(); System.out.println("عمرك: " + age); System.out.println("اسمك: " + name); }}
تطبيقان عمليان
حاسبة عمر تقريبية تقرأ الاسم ثم العمر، وبرنامج تحويل درجة الحرارة — كلاهما يستخدم الحل الصحيح لقراءة السطر:
import java.util.Scanner;public class AgeFuture { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("ما اسمك؟ "); String name = sc.nextLine(); System.out.print("كم عمرك؟ "); int age = sc.nextInt(); sc.nextLine(); System.out.println("أهلًا " + name + "!"); System.out.println("بعد 10 سنوات ستبلغ " + (age + 10) + " عامًا"); }}
import java.util.Scanner;public class ToFahrenheit { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("درجة الحرارة بالمئوية: "); double c = sc.nextDouble(); double f = c * 9 / 5 + 32; System.out.println("بالفهرنهايت: " + f); }}
int n = sc.nextInt();
String s = sc.nextLine();
System.out.println("[" + s + "]");الإخراج المنسق printf
حتى الآن طبعنا النصوص كما هي — لكن ماذا لو أردنا جدولًا بأعمدة محاذاة وأرقام بكسور موحدة؟ هنا يأتي printf.
تخيّل printf قالبًا معدنيًا: تكتب النص مرة واحدة وتترك فيه فتحات تُملأ بالقيم عند الطباعة. كل فتحة تبدأ بـ % يليها حرف يحدد نوع القيمة. جافا تحل محل كل محدد بالقيمة التالية بالترتيب:
| المحدد | يستقبل | مثال |
|---|---|---|
| %d | عدد صحيح int | printf("%d", 42) → 42 |
| %f | عدد عشري double (٦ خانات افتراضيًا) | printf("%f", 3.5) → 3.500000 |
| %.2f | عشري بدقة رقمين بعد الفاصلة | printf("%.2f", 3.14159) → 3.14 |
| %s | نص String | printf("%s", "جافا") → جافا |
| %c | حرف char | printf("%c", 'A') → A |
| %n | لا يستقبل شيئًا — سطر جديد | printf("أ%nب") → سطران |
| %% | لا يستقبل شيئًا — تطبع % نفسها | printf("%d%%", 95) → 95% |
public class PrintfDemo { public static void main(String[] args) { System.out.printf("نص: %s | عدد صحيح: %d | حرف: %c%n", "جافا", 42, 'A'); System.out.printf("عدد عشري: %f — وبدقة رقمين: %.2f%n", 3.5, 3.14159); System.out.printf("علامة النسبة: %d%%%n", 95); }}
العرض والمحاذاة — روح الجداول
بين % والحرف يمكنك كتابة رقم عرض: أقل عرض للحقل. والشرطة - قبل الرقم تجعل القيمة تلتصق باليسار بدل اليمين (مفيد مع النصوص). جرّب هذا المثال ولاحظ الأقواس — كل قيمة حشوت بمسافات حتى بلغت عرضها:
public class PrintfWidth { public static void main(String[] args) { System.out.printf("[%5d][%-5d]%n", 42, 42); System.out.printf("[%10s][%-10s]%n", "جافا", "جافا"); System.out.printf("[%10.2f][%-10.2f]%n", 3.14159, 3.14159); }}
مثال ختامي: جدول درجات منسق
الآن نجمع كل شيء: %-10s لعمود الأسماء (حقل عرضه 10 خانات تُحاذى النص فيه إلى اليسار وتُملأ بقية الخانات بمسافات، فتبدأ الأعمدة التالية دائمًا من الموضع نفسه) و%-8d للدرجات و%-8c للتقديرات — جدول كامل بثلاث جمل printf فقط:
public class GradesTable { public static void main(String[] args) { System.out.printf("%-10s %-8s %-8s%n", "الطالب", "الدرجة", "التقدير"); System.out.printf("%-10s %-8d %-8c%n", "سارة", 95, 'A'); System.out.printf("%-10s %-8d %-8c%n", "خالد", 88, 'B'); System.out.printf("%-10s %-8d %-8c%n", "ليلى", 76, 'C'); }}
المشروع التجميعي: بطاقة تعريف الطالب
حان وقت تجميع كل ما تعلمته في الوحدات الأربع: نصوص، مقاطع، أرقام، وإدخال من المستخدم — في برنامج واحد كامل ينتج بطاقة تعريف جاهزة.
الهدف: برنامج يسأل الطالب عن اسمه وصفه، ثم يطبع «بطاقة تعريف» فيها الاسم، والصف، وعدد أحرف الاسم، وأول حرف منه، و«اسم مستخدم» مولّد من أول حرفين + رقم الصف، واسم المقرر بأحرف كبيرة. كل ميزة في البطاقة تستخدم مهارة من دروس الوحدة الثالثة والرابعة — وبعضها من الوحدات الأولى.
٢) اسأل «ما صفك؟» واقرأ الإجابة بـ nextInt() في متغير grade.
٣) ابنِ اسم المستخدم: name.substring(0, 2) يلتصق برقم الصف بإشارة +.
٤) اطبع البطاقة: الاسم والصف، ثم name.length() لعدد الأحرف، وname.charAt(0) للحرف الأول.
٥) اختم باسم المقرر: خزّن "java 101" في متغير واطبعه بعد toUpperCase().
ابدأ من هذا الهيكل واملأ التعليقات بنفسك قبل أن تنظر إلى الحل:
import java.util.Scanner;public class StudentCard { public static void main(String[] args) { // TODO ١) أنشئ Scanner اسمه sc يقرأ من System.in // TODO ٢) اطبع "ما اسمك؟ " ثم اقرأ الإجابة في name بـ nextLine() // TODO ٣) اطبع "ما صفك؟ " ثم اقرأ الإجابة في grade بـ nextInt() // TODO ٤) عرّف username = أول حرفين من name يلتصقان برقم grade // TODO ٥) اطبع سطر "======== بطاقة الطالب ========" ثم الأسطر: // الاسم، الصف، عدد الأحرف name.length()، الحرف الأول name.charAt(0) // اسم المستخدم، وأخيرًا "java 101" بعد تطبيق toUpperCase() عليها }}
الحل الكامل — قارنه بمحاولتك:
import java.util.Scanner;public class StudentCard { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("ما اسمك؟ "); String name = sc.nextLine(); System.out.print("ما صفك؟ "); int grade = sc.nextInt(); String username = name.substring(0, 2) + grade; String course = "java 101"; System.out.println("======== بطاقة الطالب ========"); System.out.println("الاسم: " + name); System.out.println("الصف: " + grade); System.out.println("الأحرف في الاسم: " + name.length()); System.out.println("الحرف الأول: " + name.charAt(0)); System.out.println("اسم المستخدم: " + username); System.out.println("المقرر: " + course.toUpperCase()); }}
ما اسمك؟ ما صفك؟ ======== بطاقة الطالب ======== الاسم: سارة الصف: 3 الأحرف في الاسم: 4 الحرف الأول: س اسم المستخدم: سا3 المقرر: JAVA 101
تحدي: أين الخطأ؟
ثلاثة برامج عن الإدخال والإخراج — بعض أخطائها توقف البرنامج بصوت عالٍ، وبعضها يتسلل بصمت ويعطي نتيجة فارغة أو خاطئة. اقرأ الكود وتتبّع تدفق القراءة سطرًا سطرًا، ثم اضغط على السطر المخالف.
المقطع ١: البرنامج يسأل عن العمر ثم الاسم، لكن الترحيب يخرج بلا اسم: «أهلًا !». أين الخطأ؟
تتبّع ماذا تبقى في سطر الإدخال بعد قراءة الرقم — ومن تقرأه؟ راجع فخ «السطر المتبقي» في الدرس 4·3.
المقطع ٢: أدخلنا 2 ثم 3 ونريد مجموعهما، لكن الناتج «المجموع: 23»! أين الخطأ؟
الرقمان قُرئا بشكل سليم — فانظر إلى سطر الطباعة: من يلتصق بمن، وبأي ترتيب تُقرأ الإشارات؟ راجع قاعدة الأقواس في الدرس 1·2.
المقطع ٣: سطر printf واحد يوقف البرنامج برسالة «رمز تنسيق غير مدعوم». أين الخطأ؟
كل % في نص التنسيق تنتظر حرفًا يحدد نوع القيمة — ما الحرف الذي يلي % هنا؟ وكيف تطبع % نفسها؟
تمارين الوحدة ٤
تمارين على الإدخال والإخراج الأساسي (رموز الهروب، Scanner وفخ السطر المتبقي، والإخراج المنسق printf). كل تمرين أمامه كود ناقص: اضغط «✎ تعديل»، اكتب الكود المطلوب مكان التعليقات، اضغط «✔ حفظ» ثم «▶ تشغيل» — وقارن نتيجتك بالهدف المكتوب أسفل كل تمرين. في التمارين التي تسأل المستخدم أدخل القيم الاختبارية المكتوبة داخل التمرين حتى يطابق إخراجك الهدف.
أكمل البرنامج: اقرأ اسم المستخدم بـ nextLine() وعمره بـ nextInt()، ثم اطبع «أهلًا X، بعد 10 سنوات ستكون Y عامًا» حيث Y = العمر + 10. القيم الاختبارية: أدخل «سارة» ثم «17».
import java.util.Scanner;public class FutureAge { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // ١) اطبع "ما اسمك؟" ثم اقرأ الإجابة في name بـ nextLine // ٢) اطبع "كم عمرك؟" ثم اقرأ الإجابة في age بـ nextInt // ٣) اطبع: أهلًا (الاسم)، بعد 10 سنوات ستكون (age+10) عامًا }}
ما اسمك؟ كم عمرك؟ أهلًا سارة، بعد 10 سنوات ستكون 27 عامًا
البرنامج يقرأ رقمين ويطبع مجموعهما — لكن بين القراءتين سؤال نصي، فلو نسيت تفريغ السطر سيفشل. أكمل البرنامج مع sc.nextLine() إضافية بين القراءتين. القيم الاختبارية: أدخل «7» ثم «موافق» ثم «5».
import java.util.Scanner;public class AddTwo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // ١) اطبع "أدخل الرقم الأول: " واقرأه في a بـ nextInt // ٢) استدعِ sc.nextLine() مرة إضافية لتفريغ بقية السطر System.out.print("هل تريد المتابعة؟ "); String ok = sc.nextLine(); // ٣) اطبع "أدخل الرقم الثاني: " واقرأه في b بـ nextInt // ٤) اطبع "المجموع: " متبوعًا بمجموع a + b }}
أدخل الرقم الأول: هل تريد المتابعة؟ أدخل الرقم الثاني: المجموع: 12
اطبع بـ printf ثلاثة أسطر بالصيغة «الرقم مربعه المربع» للأرقام 1 و2 و3 — مثل «1 مربعه 1» — في كل سطر محددان %d وسطر جديد %n.
public class SquaresTable { public static void main(String[] args) { // سطر لكل رقم: اطبع الرقم ثم مربعه بمحددين %d وسطر جديد %n // الأسطر المطلوبة: 1 مربعه 1 / 2 مربعه 4 / 3 مربعه 9 }}
1 مربعه 1 2 مربعه 4 3 مربعه 9
اقرأ اسم منتج بـ next() وسعره بـ nextDouble() وكميته بـ nextInt()، ثم اطبع بـ printf سطرًا واحدًا بالصيغة [الاسم] xالكمية = الإجمالي: الاسم داخل قوسين مربعين في حقل عرضه 10 خانات محاذى لليسار (%-10s)، والإجمالي (السعر × الكمية) بدقة رقمين بعد الفاصلة. القيم الاختبارية: أدخل «Pen» ثم «2.5» ثم «4».
import java.util.Scanner;public class InvoiceLine { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // ١) اطبع "اسم المنتج:" ثم اقرأ الاسم في name بـ next() // ٢) اطبع "السعر:" ثم اقرأه في price (double) بـ nextDouble() // ٣) اطبع "الكمية:" ثم اقرأها في qty (int) بـ nextInt() // ٤) اطبع بـ printf: [الاسم بعرض 10 محاذى لليسار] xالكمية = الإجمالي بدقة رقمين }}
اسم المنتج: السعر: الكمية: [Pen ] x4 = 10.00