Menu
flag Ar iconالعربيةdown icon

الإدخال والإخراج في C++: cin وcout وgetline والتدفقات

كيف يعمل الإدخال والإخراج عبر الطرفية في C++: الطباعة باستخدام cout، والقراءة باستخدام cin، وخطأ سطر جديد الشهير في getline بعد cin، وكيفية التعافي عندما يفشل الإدخال.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

كيف يتحدث C++ مع الطرفية

يُجري C++ عمليات الإدخال والإخراج عبر الطرفية من خلال التدفقات الموجودة في الترويسة <iostream>. تكتب إلى العالم الخارجي عبر cout (إخراج المحارف)، وتقرأ من المستخدم عبر cin (إدخال المحارف). وهما يستخدمان مُعامِلَين رأيتهما من قبل في صورة إزاحة البتات، وأُعيد توظيفهما هنا:

  • << هو مُعامِل الإدراج؛ يُرسل البيانات إلى داخل cout.
  • >> هو مُعامِل الاستخراج؛ يسحب البيانات من داخل cin إلى متغير.

طريقة سهلة لتذكّر الاتجاه: الأسهم تشير إلى الجهة التي تتدفق نحوها البيانات. والآن بعد أن صرت قادراً على الاحتفاظ بالنص في السلاسل النصية، لننقل ذلك النص إلى داخل برنامجك وخارجه.

الطباعة باستخدام cout

يُرسل cout كل ما تُدرجه إلى الإخراج القياسي. يمكنك تسلسل عدة << في عبارة واحدة، مع مزج النص والأرقام والمتغيرات بحرية.

يُضاف كل << إلى السطر نفسه إلى أن تُدرج سطراً جديداً. ولديك طريقتان لفعل ذلك: '\n' يُدرج محرف سطر جديد، بينما endl يُدرج سطراً جديداً ويُفرغ المخزن المؤقت إلى الشاشة. للإفراغ تكلفة حقيقية، لذا داخل حلقة تطبع آلاف الأسطر فضّل '\n'؛ فالتدفق يُفرغ نفسه عند الحاجة (ودائماً عند إنهاء البرنامج).

// مناسب لرسالة لمرة واحدة:
cout << "Done" << endl;

// في حلقة مكثفة، فضّل هذا - دون إفراغ قسري في كل تكرار:
for (int i = 0; i < 1000000; ++i)
    cout << i << '\n';

القراءة باستخدام cin و>>

يقرأ cin >> variable رمزاً واحداً محدوداً بالفراغات ويحوّله إلى نوع المتغير. يتخطى أي فراغات أو أسطر جديدة في البداية، ثم يتوقف عند الفراغ التالي.

ولأن >> يتوقف عند الفراغ، فهو ممتاز للأرقام المفردة والكلمات الواحدة لكنه عديم الفائدة لجملة كاملة: فمع الإدخال hello world يقرأ cin >> word كلمة hello فقط ويترك world في المخزن المؤقت للقراءة التالية.

قراءة سطر كامل باستخدام getline

لالتقاط سطر كامل — بمسافاته وكل شيء فيه — استخدم getline(cin, line)، الذي يقرأ كل شيء حتى مفتاح Enter داخل std::string.

هذه هي الأداة الصحيحة كلما أمكن أن يحتوي الإدخال على مسافات — الأسماء والعناوين والجمل. وهناك مأزق واحد فقط ينتظرك في اللحظة التي تمزج فيها getline مع >>.

مأزق السطر الجديد بين cin و getline

هذا هو أكثر أخطاء الإدخال والإخراج شيوعاً في C++ على الإطلاق. عندما تنفّذ cin >> n، يقرأ الاستخراج الرقم لكنه يترك السطر الجديد (مفتاح Enter الذي ضغطته) في مخزن الإدخال المؤقت. يرى getline التالي ذلك السطر الجديد المتبقي على الفور، ويعدّ السطر منتهياً بالفعل، ويسلّمك سلسلة فارغة — دون أن يتوقف لحظة بانتظار الإدخال.

int age;
string city;

cin >> age;            // تكتب 30 وتضغط Enter؛ يبقى '\n' في المخزن المؤقت
getline(cin, city);    // يقرأ '\n' المتبقي -> تصبح city "" (فارغة!)

الحل هو التخلص من ذلك السطر الجديد المتبقي باستخدام cin.ignore بعد >> وقبل getline:

يتخطى cin.ignore(numeric_limits<streamsize>::max(), '\n') المحارف إلى أن يبتلع سطراً جديداً (أو يبلغ نهاية الإدخال). هذه هي النسخة المتينة؛ أما الأقصر cin.ignore() فيتخلص من محرف واحد فقط، وهو ما ينهار إذا أدخل المستخدم مسافات إضافية بعد الرقم. اعتد على استخدام الصيغة الكاملة.

عندما يفشل الإدخال

إذا أدخل المستخدم حروفاً في موضع توقّعت فيه رقماً، يفشل الاستخراج: يدخل cin في حالة خطأ ويبقى المتغير الهدف دون تغيير (يُضبط إلى 0 منذ C++11). والأسوأ أنه ما إن يدخل cin في حالة الفشل، يُتخطى أيضاً كل قراءة لاحقة بصمت، فقد تنتهي بك الحال في حلقة لا نهائية.

التعافي يتكون دائماً من خطوتين: cin.clear() يعيد ضبط أعلام الخطأ ليصبح التدفق قابلاً للاستخدام من جديد، وcin.ignore(...) يتخلص من المحارف المسيئة التي ما زالت عالقة في المخزن المؤقت. تجاهل ignore يُبقي الإدخال السيئ في مكانه، فيفشل >> التالي مجدداً — الحلقة اللانهائية الكلاسيكية. ويعمل فحص cin >> n مباشرة في الشرط لأن التدفق يتحوّل إلى false عندما يكون في حالة فشل.

أخطاء شائعة يجب تجنّبها

  • استخدام cin >> s لجملة. يتوقف عند أول فراغ. استخدم getline لأي شيء يحتوي على مسافات.
  • نسيان cin.ignore بين >> وgetline. السطر الجديد المتبقي يعطيك سطراً فارغاً. نظّف المخزن المؤقت أولاً.
  • اللجوء إلى endl في كل مكان. كل واحدة منها تفرض إفراغاً. اجعل '\n' هو الافتراضي، واحتفظ بـendl للحظات التي يجب فيها فعلاً ظهور الإخراج الآن.
  • تجاهل cin الفاشل. الحروف في قراءة عددية تُعطّل cin؛ نفّذ دائماً clear() ثم ignore() قبل القراءة من جديد.

التالي: تدفقات السلاسل النصية

يلتقي الإدخال والإخراج عبر الطرفية مع معالجة السلاسل النصية في تدفقات السلاسل النصية. يمنحك stringstream المُعامِلين << و>> نفسيهما، لكن موجّهَين إلى سلسلة نصية في الذاكرة بدلاً من الطرفية — وهو مثالي لتحليل سطر إلى أرقام، وبناء نص منسّق، والتحويل بين السلاسل النصية والأنواع الأخرى دون لمس لوحة المفاتيح إطلاقاً.

الأسئلة الشائعة

لماذا يتخطى getline الإدخال مباشرة بعد استخدام cin في C++؟

يقرأ cin >> x الرقم لكنه يترك السطر الجديد الذي ضغطته في المخزن المؤقت. يقرأ getline التالي حتى ذلك السطر الجديد المتبقي ويعيد سلسلة فارغة على الفور. نظّفه أولاً باستخدام cin.ignore(numeric_limits<streamsize>::max(), '\n'); بعد >> وقبل getline.

ما الفرق بين endl و\n في C++؟

كلاهما ينهي السطر، لكن endl يقوم أيضاً بـإفراغ (flush) مخزن الإخراج إلى الشاشة، بينما '\n' يدرج سطراً جديداً فقط. للإفراغ تكلفة؛ ففي حلقة مكثفة فضّل '\n' واترك التدفق يُفرغ نفسه بنفسه. استخدم endl فقط حين تحتاج فعلاً إلى ظهور الإخراج في هذه اللحظة.

كيف أقرأ سطراً كاملاً من النص بما في ذلك المسافات في C++؟

استخدم getline(cin, line)، وليس cin >> line. يتوقف المُعامِل >> عند أول فراغ، لذا يلتقط كلمة واحدة فقط. أما getline فيقرأ كل شيء حتى مفتاح Enter داخل std::string، بما في ذلك المسافات.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن