الفئة iostream وفئات _withassign في ++C
الفئة iostream وفئات _withassign
الفئة iostream المشتقة من كل من ostream و istream معا تعمل فقط كفئة أساسية لعدد من الفئات المشتقة منها , أهمها iostram_withassign وليس لها دوال خاصة بها (عدا بادئتها ومنهياتها) .
ولدينا ثلاثة فئات في هذه المجموعة _withassign
Iostream_withassign مشتقة من الفئة istream
ostream_withassign مشتقة من الفئة ostream
هذه الفئات تماثل الفئات المشتقة منها , عدا أن بها مؤثرات تخصيص مزادة التحميل بحيث يمكن نسخ كائناتها .
لماذا نحتاج إلي فئات قابلة للنسخ وأخري غير قابلة لذلك ؟ بشكل عام , ليس مستحبا نسخ كائنات التدفقات . والسبب هو أن كل كائن منها مصحوب بكائن streambuf معين يحتوي علي حجم من الذاكرة تكفي لبيانات الكائن . فإذا ما نسخت كائنا , تسببت في بلبلة إذا نسخ معه الكائن streeambuf علي أنه في بعض الحالات القليلة يستلزم الأمر نسخ كائن لتدفق , كما في حالة إعادة التوجيه للكائنين << و >> سوف نتحدث عن ذلك لاحقا ) .
ولهذا السبب وضعت الفئات istream, ostream, iostream لا تقبل النسخ ( يجعل بادئات النسخ ومؤثرات النسخ الخاصة بها والمزادة التحميل خاصة ) , ومعها الفئات _withassign المشتقة منها تقبل ذلك .
كائنات التدفق سابقة التعريف
استخدمنا إلي الأن بكثرة أشهر كائنين من مجموعة الفئات _withassign , وهما cin, المشتق من الفئة istream_withassign و cout المشتق من الفئة ostream_withassign ويعملان عادة مع لوحة المفاتيح والشاشة . أما الكائنان الأخران فهما : cerr و clog المشتقان أيضا من الفئة ostream_withassign .
يستخدم الكائن cerr غالبا لرسائل الأخطاء وعمليات تحديدها . وتخرج رسائله فورا , ولا تقبل إعادة التوجيه . ولذلك فلديك فرصة أن تري رسائله إذا انقطع سير برنامجك قبل الأوان . أما الكائن الأخر clog فهو لا يقبل إعادة التوجيه أيضا , إلا أن خرجه يخزن في ذاكرة مرحلية .
تدفق الأخطاء
إلي الأن , استخدمنا فقط أسلوبا مباشرا إلي حد ما للإدخال والإخراج , مستخدمين تعبيرات من قبيل :
Cout << ''Good morning'':
Cin >> var;
علي أننا بذلك نفترض أن شيئا ما شاذا لن يحدث بالمرة خلال الإدخال أو الإخراج . وليس هكذا الحال دائما , ماذا يحدث حينما تدخل العبارة النصية nine بدلا من بيان العدد الصحيح 9 ؟ أو حين تضرب مفتاح الإدخال دون إدخال شئ ؟ أو حينما يحدث خطأ في أي من المكونات المادية ؟ في هذا القسم سوف نستكشف هذه المشاكل , وسوف نري العديد من التكنيكات التي تسري أيضا علي عمليات الملفات .
بتات حالات الأخطاء
تسجل بتات حالات الأخطاء وهي أحد عناصر الفئة ios من نوع enum , ما يحدث من أخطاء خلال عمليتي الإدخال والإخراج . ويبين الشكل كيف تبدو هذه البتات ويتضمن الجدول معني كل بته والجدول بعض الدوال التي يمكنها أن تقرأ أو حتي تتحكم في هذه البتات .
شكل بتات أخطاء التدفق
جدول بتات الأخطاء
اسم البتة | معناها |
Goodbit | لا أخطاء ( كافة البتات في الوضع 0 ) |
eofile | الوصول لانتهاء الملف |
Failbit | خطأ من المستخدم , إنهاء قبل الأوان |
badbit | عملية غير معتمدة (لا تدفق لها) |
Hardfail | خطأ لا يمكن إصلاحه |
جدول دوال بتات الأخطاء
الدالة | الغرض منها |
Int = eof(); | إعادة true في حالة نهاية الملف |
Int = fail(); | إعادة true في حالة قيمة 1 لأي من بتات الأخطاء الثلاثة |
Int = bad(); | إعادة true في حالة قيمة 1 للبتة badbit أو failbit |
Int = good(); | إعادة true في حالة كافة البتات = 0 |
Clear(int=0); | في حالة عدم وجود معامل البتات توضع في القيمة 0 , في غير ذلك , توضع البتة المقصودة في الوضع 1 , مثال : clear(ios::failbit) |
مثال : إليك مثالا لبرنامج يدخل أرقاما صحيحة , ويعطيك رسالة تحذير حين تدخل شيئا غير الأرقام الصحيحة
#include<iostream.h>
Main()
{
Int I;
While(1) // cycle until input ok.
{
Cout << ''\nEnter an integer'';
Cin >> I;
If (cin.good() ) // if no errors
{
Cin.ignore(10, '\n'); //remove new line
Break; // exit loop
}
Cin.clear();
Cout << ''Incorrect input'';
Cin.ignore(10, '\n'); // remove new line
}
Cout << ''Integer is: '' << I;
}
تستمر الدوارة while طالما أنك ترتكب الخطأ بأن تدخل بدلا من الأرقام , وفي كل مرة تخرج لك رسالة تحذرك من أنك ارتكبت خطأ وفي حالة إدخال عدد صحيح , يقبلها البرنامج وينتهي .
إدخال عدد كبير من المحارف
إذا كان عدد المحارف المدخلة لتدفق الإدخال كثيرا , فقد يسبب ذلك مشكلة خاصة إذا كان إدخالها بطريق الخطأ كما في مثالنا , فمن الأمور المعتادة أن تظل المحارف الزائدة باقية في التدفق بعد أن يكون الإدخال قد انتهي فرضا وقد تدخل المحارف المتخلفة للعملية التالية التي لا شأن لها بها . فحتي يمكن التخلص من مثل هذه المحارف , تستخدم الدالة ignore() لكي تقرأ وتلقي بعيدا إلي عدد 10 محارف بما فيها المحدد المذكور في دالتها فمثلا يؤدي الأمر التالي إلي قراءة عدد أقصي هو 10 محارف بما فيها السطر الجديد , بواسطة cin ويلغيها من الإدخال :
Cin.ignore(10, '\n');
تجاهل المسافات البيضاء
عادة ما يتم تجاهل المسافات البيضاء عند إدخال الأرقام , وقد يؤدي ذلك لبعض التأثرات المزعجة , ففي برنامجنا المعطي , إذا ضرب المستخدم علي مفتاح الأدخال دون أن يدخل رقما , فإن المشيرة تنتقل إلي السطر الجديد , ويظل البرنامج منتظرا إدخال الرقم المطلوب . وقد يسبب ذلك بالأمر الخطير في الحالات المعتادة , ولكن في البرامج الرسومية مثلا , قد يسبب ذلك تشويه منظر الشاشة , الأمر الذي يتطلب أمرا ألا يتجاهل البرنامج المسافة البيضاء , لتدخل في سياق المحارف الخاطئة . وقد يكون أمر كهذا مفيدا في تلاقي هذا الاحتمال :
Cin.unset(ios::skipws);
كما سنعرض لك في المثال التالي .
أيضا لتجاهل المسافات البيضاء أهمية عندما ندخل محارف من ملفات , ففي حالة إدخال أرقام خطأ بدلا من محارف من لوحة المفاتيح , لا يسبب الأمر مشكلة , حيث يمكن دائما اعتبار الأرقام المدخلة من لوحة المفاتيح نوعا من المحارف . أما حينما يكون الإدخال من ملف , فإن الأمر يختلف , حيث يتم الاختبار علي المحارف المدخلة لمعرفة إن كان المخل هو محرف نهاية الملف أو غير ذلك من أمور أخطر . هذا أيضا قد يكون من الملائم تجاهل المسافات البيضاء .
برنامج المسافات الإنجليزية مع تتبع الأخطاء
سوف نقدم لك في المثال التالي برنامجا يتقبل المسافات بالوحدات الإنجليزية , ويصدر رسائل أخطاء حين لا يكون الإدخال سليما كأن تدخل بصورة خلاف الأعداد الصحيحة , أو تكون البوصات أكبر من 12 , أو أقل من 0 .
englerr.cpp
// englerr.cpp
// input checking with English Distance class
// UCS Laboratories
#include <iostream.h>
#include <string.h> // for strchr()
#include <stdlib.h> // for atoi()
#include <conio.h>
int isint(char*); // prototype
const int IGN = 10; // characters to ignore
class Distance // English Distance class
{
private:
int feet;
float inches;
public:
Distance() // constructor (no args)
{ feet = 0; inches = 0.0; }
Distance(int ft, float in) // constructor (two args)
{ feet = ft; inches = in; }
void showdist() // display distance
{ cout << feet << "\'-" << inches << '\"'; }
void getdist(); // get length from user
};
void Distance::getdist() // get length from user
{
char instr[80]; // for input string
while(1) // cycle until feet are right
{
cout << "\n\nEnter feet: ";
cin.unsetf(ios::skipws); // do not skip white space
cin >> instr; // get feet as a string
if( isint(instr) ) // is it an integer?
{ // yes
cin.ignore(IGN, '\n'); // eat chars, including newline
feet = atoi(instr); // convert to integer
break; // break out of 'while'
} // no, not an integer
cin.ignore(IGN, '\n'); // eat chars, including newline
cout << "Feet must be an integer\n"; // start again
} // end while feet
while(1) // cycle until inches are right
{
cout << "Enter inches: ";
cin.unsetf(ios::skipws); // do not skip white space
cin >> inches; // get inches (type float)
if(inches>=12.0 || inches<0.0)
{
cout << "Inches must be between 0.0 and 11.99\n";
cin.clear(ios::failbit); // "artificially" set fail bit
}
if( cin.good() ) // check for cin failure
{ // (most commonly a non-digit)
cin.ignore(IGN, '\n'); // eat the newline
break; // input is OK, exit 'while'
}
cin.clear(); // error; clear the error state
cin.ignore(IGN, '\n'); // eat chars, including newline
cout << "Incorrect inches input\n"; // start again
} // end while inches
}
int isint(char* str) // return true if the string
{ // represents type int
int slen = strlen(str); // get length
if( slen==0 || slen > 5) // if no input, or too long
return 0; // not an int
for(int j=0; j<slen; j++) // check each character
// if not digit or minus
if( (str[j] < '0' || str[j] > '9') && str[j] != '-' )
return 0; // string is not an int
long n = atol(str); // convert to long int
if( n<-32768L || n>32767L ) // is it out of int range?
return 0; // if so, not an int
return 1; // it is an int
}
void main()
// UCS Laboratories
{
Distance d; // make a Distance object
char ans;
do
{
d.getdist(); // get its value from user
cout << "\nDistance = ";
d.showdist(); // display it
cout << "\nDo another (y/n)? ";
cin >> ans;
cin.ignore(IGN, '\n'); // eat chars, including newline
} while(ans != 'n'); // cycle until 'n'
gethce();
}
وقد لجانا إلي حيلة بسيطة هنا إذ قمنا بتشغيل البتة failbit عن طريق البرنامج في حالة مختلفة البوصات للسروط المطلوبة , وذلك بالأمر :
Cin.clear(ios::failbit);
فحين يختبر البرنامج الشرط cin.good() فإنه يجد أن أحد البتات ليست 0 وبالتالي يصدر إشارة الخطأ .
تعليقات
إرسال تعليق