ملخص
يفتتح هذا الدرس الوحدة السابعة من الدورة، المخصّصة للنصوص والملفات. ويشرح الغاية من الوحدة: تعليم معاملة النص بوصفه معطى، بترميزه الحقيقي، وجعل البرنامج يقرأ الملفات ويكتبها دون أن ينهار أمام مدخلات مشوَّهة؛ ويعرض دروسها الأربعة والخيط الذي يجمعها؛ ويذكّر بما تفترض الوحدة أنه معلوم، ولا سيما أن المحرف عدد في نظر الآلة، وبالمتجهات والسجلات والدوال من الوحدتين 5 و6؛ ويصوغ المسألة التي تحلّها الوحدة، وهي مسألة برنامج يبدو صحيحًا لكنه يفقد بيانات دون تنبيه، ويتوقف عند أول سطر غير متوقع، ويقيس خطأً الأسماء المكتوبة بحروف خارج ASCII؛ ويستبق أن الحرف لا يشغل دائمًا بايتًا واحدًا؛ ويبيّن إلى أين تقود الوحدة، في الوحدة 8 وفي دورة البرمجة بلغة C++ II.
أهداف التعلم
- وصف تسلسل محتويات الوحدة وشرح ما يقدّمه كل درس للدرس الذي يليه.
- تذكّر تمثيل المحارف برموز عددية (الدرس 2.4)، وكتابة رمز بالنظام الثنائي وبالنظام الست عشري.
- استباق، بمثال، أن الحرف ليس دائمًا بايتًا واحدًا، وأن عدّ البايتات لذلك ليس عدًّا للحروف.
- التعرّف على ما يُنتظر إتقانه عند إنهاء الوحدة، وتمييزه ممّا سيُتعلَّم في الوحدة اللاحقة وفي دورة البرمجة بلغة C++ II.
تقييم الدخول
قبل اجتياز الوحدة يحسن قياس نقطة البداية. وهذا التقييم تقييم ذاتي: لا تُمنح عليه درجة، وغايته الوحيدة توجيه الدراسة. وتتناول الأسئلة الستة الأولى ما تفترض الوحدة أنه معلوم: رمز المحرف، وكتابة عدد بالنظامين الثنائي والست عشري، وقناع البتات، والمرور على متجه يرشّح عناصره، وما يحدث حين تتلقى القراءة حروفًا بدل عدد، وتعريف السجل. وتتناول الأسئلة الأربعة التي تليها، بترتيب الدروس، الأفكار المركزية في الوحدة: ما يعيده بحث لا يجد شيئًا، وحجم كلمة فيها حرف خارج ASCII، والسطر الفارغ الذي يظهر عند المزج بين طريقتين في القراءة، والملف الذي تعذّر فتحه. وليس أحد ملزمًا بأن يصيب في هذه الأربعة الآن، وفي بعضها يكون «لا أعرف بعدُ» جوابًا صادقًا: فكل جواب، صائبًا كان أو خاطئًا، يشرح المسألة ويسمّي الدرس الذي يعالجها. وتُقاس المهارات نفسها مرة أخرى في النهاية، في درس الخلاصة.
قبل أن تبدأ الوحدة، قِس نقطة انطلاقك. إنه تقييم ذاتي قصير: لا يُمنح عليه درجة، وتُحفظ نتيجته في متصفحك فقط.
السؤال 1من وحدات سابقةماذا يكتب
std::println("{:d} {:d}", 'A', 'a');؟يظن أن الأحرف الصغيرة تلي الكبيرة مباشرة؛ وبينهما 32 رمزًا، تتخللها بعض العلامات.يغفل أن{:d}تطلب الرمز؛ ولولاها لكتبتstd::printlnالحرف.يعكس الترتيب: ففي ASCII رموز الأحرف الكبيرة أصغر من رموز الصغيرة.يحفظcharعددًا، هو رمز المحرف، و{:d}تطلب كتابة هذا العدد لا الحرف. وفي ASCII تبدأ الأحرف الكبيرة من 65 والصغيرة من 97: والفرق 32، أي بت واحد. وينطلق درس النص ليس ASCII من هنا ليشرح ما يحدث للحروف التي ليست في ASCII.السؤال 2من وحدات سابقةكيف يُكتب 233 بالنظام الثنائي وبالنظام الست عشري؟
يخطئ في تجميع البتات الأربع الدنيا:1001هو 9 لا 1.يقرأ البتات معكوسة، من الأقل أهمية إلى الأكثر أهمية.يخلط بين الكتابة الست عشرية والعشرية.. وإذا جُمعت البتات أربعًا أربعًا من اليمين، كانت1110و1001هما 14 و9، أيeو9. وهذا رمز الحرف «é»، وستبيّن الوحدة أن هذا الرمز لا يُحفظ في ملف نصي حديث بايتًا واحدًاe9، بل بايتين.راجِع: الأعداد الصحيحة وتمثيلها
السؤال 3من وحدات سابقةكم تساوي
0xB1 & 0xC0؟يخلط بين&و|: فـ«أو» البتّية تُشغّل البتات العلوية بدل أن تنتقيها.يظن أن القناع لا يغيّر شيئًا؛ وهو يمحو البتات الست الدنيا.يخلط بين&و^، أي «أو» الحصرية.0xB1هو10110001و0xC0هو11000000: و«و» البتّية لا تُبقي إلا البتّين العلويين من0xB1، وهما10. وهذا القناع هو الذي يستعمله درس النص ليس ASCII للتعرّف على البايتات التي تُكمل حرفًا.راجِع: البتات والمكتبة الرياضية
السؤال 4من وحدات سابقةماذا يكتب
std::vector<int> v{4, 7, 10, 3}; std::vector<int> w{}; for (const int x : v) { if (x % 2 == 0) { w.push_back(x); } } std::println("{}", w);؟يعكس الشرط:x % 2 == 0صحيح للأعداد الزوجية.يظن أنpush_backتنسخ المتجه كله، أو أن الترشيح يُطبَّق علىvثم يُكتبv.يظن أنpush_backتضيف في البداية؛ وهي تضيف في النهاية.تمرّ الحلقة علىvوتنسخ فيwالأعداد الزوجية وحدها، بالترتيب الذي تظهر به؛ ولا يتغيّرv. وتستعمل الوحدة المخطط نفسه مع النص: المرور على بايتات سلسلة نصية والإبقاء على بعضها، أو المرور على أسطر ملف والإبقاء على الصالحة منها.راجِع: المتجهات
السؤال 5من وحدات سابقةبعد
int n{-1}; std::cin >> n;يكتب المستخدمabc. كم تساويn، وماذا يحدث للقراءات اللاحقة؟يظن أن القراءة الفاشلة لا تترك أثرًا؛ وهي تتركه في المتغير وفي حالة المجرى.يفترض أن C++ تنبّه إلى المدخلات غير الصالحة؛ والبرنامج يمضي كأن القراءة نجحت.يخلط بين قراءة عدد صحيح وقراءة محرف.تتخطى القراءة المسافات، فتجد حرفًا لا يمكن أن يبدأ به عدد، فتفشل: والمعيار يقضي بأن يأخذ المتغير 0، وأن يبقى الحرف في المجرى، وأن يبقى المجرى في حالة خطأ، دون أي رسالة. ويعلّم درس قراءة المدخلات بإحكام التمييز بين هذا الفشل ونهاية البيانات، والاستدراك بعده.راجِع: المدخلات والمخرجات والأخطاء
السؤال 6من وحدات سابقةأيّ التعريفات الآتية يصرّح تصريحًا صحيحًا بسجل فيه عنوان وسنة؟
ينسى الفاصلة المنقوطة بعد القوس الذي يغلق التعريف، وهو أول خطأ يقع فيه من يكتب السجلات؛ والمترجم يشير إليه بدقة.يفصل بين الحقول بفواصل، كعناصر قائمة؛ وكل حقل تصريح ينتهي بفاصلة منقوطة.يخلط بين السجل، بحقوله ذات الأنواع المختلفة والأسماء، والمتجه الذي عناصره كلها من نوع واحد.يُصرَّح بكل حقل كما يُصرَّح بمتغير، بنوعه واسمه وقيمته الابتدائية بين قوسين معقوصين، وينتهي التعريف بفاصلة منقوطة بعد القوس المعقوص الذي يغلقه. ويملأ درس الملفات النصية متجهات من سجلات كهذا بأسطر ملف.راجِع: السجلات
السؤال 7إذا كانت
sهيstd::string s{"casa azul"};، فماذا تعيدs.find("x")؟هذا عرف لغات أخرى؛ والنوعstd::size_tعديم الإشارة، و-1المحوَّل إليه هوnposبالضبط، لكن مقارنة النتيجة بـ-1لا تنجح إلا بفضل هذا التحويل، وتثير تحذيرًا من المترجم.يخلط بين «غير موجود» والموضع 0، وهو موضع ظهور في بداية السلسلة النصية.هذا جواب صادق: الدرس 7.1 يقدّمfindوnpos.تعيدfindموضعًا من النوعstd::size_t، وحين لا تجد شيئًا تعيد قيمة لا يمكن أن تكون لأي موضع:npos، التي تساوي مع 64 بتًا . ويعلّم درس السلاسل النصية مقارنتها دائمًا مقارنة صريحة.راجِع: السلاسل النصية
السؤال 8ماذا يكتب
std::println("{}", std::string{"año"}.size());في برنامج محفوظ بترميز UTF-8، مثل برامج هذه الدورة؟يعدّ الحروف؛ وsizeتعدّ البايتات التي كُتبت بها.يظن أن كل حرف يشغل بايتين؛ وحروف ASCII تشغل بايتًا واحدًا.هذا جواب صادق: الدرس 7.2 يشرحه بايتًا بايتًا.تعدّsizeالبايتات لا الحروف: ففي الكلمة الإسبانية «año» (سنة) يشغل «a» و«o» بايتًا واحدًا لكل منهما، ويشغل «ñ»، وهو ليس في ASCII، بايتين في UTF-8. ويشرح درس النص ليس ASCII السبب ويعلّم عدّ الحروف.راجِع: النص ليس ASCII
السؤال 9ينفّذ برنامج
int age{}; std::string name{}; std::cin >> age; std::getline(std::cin, name);، فيكتب المستخدم36ويضغط Enter، ثم يكتبليلىويضغط Enter. ماذا يبقى فيname؟يفترض أنgetlineتبدأ من السطر الذي يلي؛ وهي تبدأ حيث توقف>>.يظن أنgetlineتعيد قراءة ما استهلكه>>من قبل.هذا جواب صادق: الدرس 7.3 يبدأ بهذا العيب.يقرأ>>العدد 36 ويترك في المجرى محرف السطر الجديد الذي كان يليه؛ وتقرأstd::getlineحتى أول محرف سطر جديد، وهو هذا، وتعيد ما كان قبله: لا شيء. ويشرح درس قراءة المدخلات بإحكام ذلك ويتجنّبه بـstd::ws.راجِع: قراءة المدخلات بإحكام
السؤال 10ينفّذ برنامج
std::ifstream in{"data.txt"}; int x{-1}; in >> x;، لكنdata.txtغير موجود في المجلد الذي يُشغَّل منه. ماذا يحدث؟يفترض أن فتح ملف غير موجود خطأ تُبلغ عنه C++؛ والبرنامج يمضي دون تنبيه.يخلط بين الفتح للقراءة والفتح للكتابة، الذي يُنشئ الملف فعلًا.هذا جواب صادق: الدرس 7.4 يبدأ بهذا التحقق.قد يفشل فتح الملف، ولا يظهر الفشل إلا بالاستعلام عن المجرى: ولهذا يُكتبif (!in)قبل القراءة. ويجعل درس الملفات النصية من هذا التحقق قاعدة، ويشرح المجلد الذي يتوقف عليه البحث.راجِع: الملفات النصية
جارٍ تحميل المحتوى…