تدفقات عمليات الملفات في ++C
تدفقات عمليات الملفات
تتطلب أغلب البرامج أن تخزن بيانات في ملفات علي الأقراص , وأن تقرأها منها . ويحتاج التعامل مع الملفات إلي طائفة أخري من الفئات ifstrea, ofstream, iofstream , للإدخال والإخراج والإثنين معا علي الترتيب . ويمكن أن يلحق كائنات هذه الفئات بالملف , لنستخدم دوالها في الكتابة في الملف والقراءة منه.
وإذا عدنا للشكل سنجد أن ifstrea مشتقة من istrea , وأن ofstream مشتقة من ostream وأن iofstream مشتقة من iostream وفئات الأباء كما تعلم مشتقة جميعها من الجد الأعلي ios . وعلي لك فإن فئات التعامل مع الملفات تستخدم كافة الدوال المنتمية للفئات الأعلي منها . كما أن فئات التعامل مع الملفات مشتقة أيضا بأسلوب التوارث المتضاعف , من الفئة ftsrambase ولهذه الفئة كائن يسمي filebuf وهو ذاكرة مرحلية مختصة بالملفات , ودواله مشتقة من فئة أعلي هي streambuf وليس علي المبرمج في الواقع عبء شغل نفسه بفئات الذاكرات المرحلية وتنظيماتها .
والفئات المتعاملة مع الملفات معرفة في الملف التصديري fstream.h وهو يحتوي بدوره علي الملف iostream.h فليس في حاجة للنص علي تضمين البرنامج هذا الملف صراحة .
وإذا كنت علي دراية بلغة السي التقليدية فسوف تجد التعامل مع الملفات هنا مختلف تماما , فالدوال القديمة مثل fread(), fwrite() , وإن كانت لا تزال سارية المفعول في لغة السي++ , وإلا أنها غير مناسبة لبيئة البرمجة الكائنية .
العمليات المهيأة
في العمليات المهيأة formatted تخزن الأرقام علي الأقراص كسلسلة من المحارف . فالعدد 6.02 مثلا بدلا من أن يخزن علي صورة العدد الكسري في 4 بايت , يخزن كالتالي : '6', '.', '0' , '2' , إي أربعة محارف متجاورة وإذا كان العدد كبيرا فمن الطبيعي ألا يكون ذل في صالح اقتصاد استخدام الذاكرة , ولكنه مناسب في بعض التطبيقات , حيث لا يكون هناك فرق بين اختزال الأرقام و المحارف .
الكتابة في الملف
البرنامج التالي يكتب محارف وأعداد صحيحة وأعداد من النوع المضاعف وعبارتين نصيتين .
formato.cpp
// formato.cpp
// writes formatted output to a file, using <<
#include <fstream.h> // for file I/O
#include <conio.h>
// (includes iostream.h)
void main()
{
char ch = 'x';
int j = 77;
double d = 6.02;
char str1[] = "Kafka"; // strings without
char str2[] = "Proust"; // embedded spaces
ofstream outfile("fdata.txt"); // create ofstream object
outfile << ch // insert (write) data
<< j
<< ' ' // needs space between numbers
<< d
<< str1
<< ' ' // needs spaces between strings
<< str2;
getche();
}
في هذا البرنامج أنشأنا كائنا outfile منتم للفئة ofstream , واستهللنا للملف المطلوب fdata هذا الاستهلال يستثير كافة أليات التعامل مع الملفات في نظام التشغيل , فيقوم أولا بفتحه علي القرص , فإذا لم يكن الملف موجودا بالفعل , أنشأه, وإن كان موجودا, محي ما عليه وكتبت عليه البيانات الجديدة ويعمل الكائن بقدر كبير ما يفعله الكائن cout في البرامج السابقة , وهو ما يمكننا أن نستخدم المؤثر << لإخراج الأنواع الأساسية من البيانات إلي الملفات.
وبعد انتهاء البرنامج , يتلاشي الكائن outfile وتقوم الدالة المنهية له بإغلاق الملف ولذا فلسنا في حاجة لغلقه بأمر صريح .
ويتضمن البرنامج عدة عيوب متعلقة بهيئة البيانات , أولا ؛ يجب الفصل بين الأرقام بمسافة , فيما أن الأرقام تخزن كسلسلة من المحارف , وليس كبيان له حقل ذو طويل ثابت , فليس من الممكن أن يعرف مؤثر الإخراج انتهاء الرقم عند قراءته إلا بهذه الطريقة . ولنفس السبب يجب فصل العبارات النصية بهذه الطريقة . معني ذلك العبارات النصية لا يمكن أن تتضمن مسافات داخلها .
ويمكنك أن تتأكد من وجود الملف باستخدام الأمر type الخاص بالدوس أو عن طريق برنامج المفكرة للويندوز .
القراءة من الملف
يمكننا أن نقرأ الملف الذي أنشأناه عن طريق كائن للفئة ifstream نستهله باسم الملف , فيفتح تلقائيا بمجرد إنشاء الكائن , وتستخلص بياناته عن طريق الكائن , ثم نظهرها بعد ذلك باستخدام المؤثر << والكائن cout .
وإليك البرنامج المطلوب .
formati.cpp
// formati.cpp
// reads formatted output from a file, using >>
#include <fstream.h>
#include <conio.h>
const int MAX = 80;
void main()
{
char ch;
int j;
double d;
char str1[MAX];
char str2[MAX];
ifstream infile("fdata.txt"); // create ifstream object
// extract (read) data from it
infile >> ch >> j >> d >> str1 >> str2;
cout << ch << endl // display the data
<< j << endl
<< d << endl
<< str1 << endl
<< str2 << endl;
getche();
}
في هذا البرنامج يقوم الكائن infile بقدر كبير ما يفعله الكائن cin في برنامجنا السابقة , فإذا كنا قد راعينا هيئة البيانات جيدا في مرحلة الكتابة , فلن نجد مشاكل في عملية استخلاص البيانات من الملف عن طريق الكائن المذكور مع المؤثر >> ثم قراءتها علي الشاشة باستخدام المؤثر << .
ومن الطبيعي أن تعاد الأرقام لصورتها الرقمية التي هي معرفة بها ] جرب تعرف المتغير j علي أنه char ستري أن المخرج هو حرف M وهو الحرف رقم 77 في جدول الأسكي [ .
العبارات النصية ذات المسافات
الأسلوب الذي انتهجناه في البرنامج السابق ليس صالحا للعبارات النصية التي تحتوي علي مسافات فاصلة , وحتي تحقق ذلك , عليك أن تستخدم محارف محددات معينة بعد كل مسافة , وفي برنامجنا ندخل للملف بعض العبارات التي تحتوي علي مسافات , وكالعادة , يحتوي كل سطر علي محرف نهاية السطر .
oline.cpp
// oline.cpp
// file output with strings
#include <fstream.h> // for file functions
#include <conio.h>
void main()
{
ofstream outfile("TEST.TXT"); // create file for output
// send text to file
outfile << "I fear thee, ancient Mariner!\n";
outfile << "I fear thy skinny hand\n";
outfile << "And thou art long, and lank, and brown,\n";
outfile << "As is the ribbed sea sand.\n";
getche();
}
ولاستخلاص هذه العبارة من الملف تمهيدا لقراءتها ننشئ الكائن المعتاد infile الذي يقوم بعملية الاستخلاص , ثم نستخدم الدالة getline() بدلا من المؤثر >> , وهي من دوال الفئة istream وتحتوي علي ذاكرة مرحلية , تحدد في المعامل الأول لها , تقرأ فيها حروف كل سطر , بما فيها المسافات , حتي تلاقي محرف نهاية السطر , ويحدد المعامل الثاني للدالة حجم الذاكرة المرحلية المطلوبة , ثم تظهر محتويات الذاكرة المرحلية يعد كل سطر يقرأ . وإليك برنامج القراءة :
iline.cpp
// iline.cpp
// file input with strings
#include <fstream.h> // for file functions
#include <conio.h>
void main()
{
const int MAX = 80; // size of buffer
char buffer[MAX]; // character buffer
ifstream infile("TEST.TXT"); // create file for input
while( !infile.eof() ) // until end-of-file
{
infile.getline(buffer, MAX); // read a line of text
cout << buffer << endl; // display it
}
getche();
}
ويظهر الخرج علي الشاشة كما كتبت في الملف . والبرنامج لا يعلم مسبقا كم من العبارات سوف يقرأ , ولذا فهو في القراءة إلي أن يصادف نهاية الملف وبالمناسبة , هذا البرنامج لا يصلح لقراءة لفات متقدمة , حيث يدخل البرنامج في دوامة إذا لم يصادف البرنامج محرف نهاية السطر صراحة .
إستكشاف نهاية الملف
علمنا أن الكائنات المنتمية إلي الفئة ios تحتوي علي بتات يمكن أن تختبر لتحديد سلامة التشغيل . وحينما نقرأ ملفا شيئا فشيئا كما نفعل هنا , فإننا سوف نصل بالضرورة إلي حالة نهاية الملف (EOF) , وهي إشارة ترسلها المكونات المادية للجهاز إلي البرنامج لتخبره بأنه لم يعد شئ يقرأ . وفي برنامجنا السابق كان بإمكاننا أن نجعل شرط استمرار الدوارة while هو عدم لقاء نهاية الملف علي الوجه التالي :
While ( !infile.eof() ) // until eof encountered
علي أن ذلك سوف يحرمنا من اختبار البتات الأخري , مثل badbit, failbit , وحتي تغطي كافة الاحتمالات , كان بإمكاننا أن نجعل الشرط علي النحو التالي :
While ( infile.good() ) // until any error encountered
إلا أننا يمكننا أن نختبر التدفق بطريقة مباشرة , فكل كائن تدفق , مثل infile يحمل قيمة يمكن اختبارها , وتكون 0 في حالة مصادفة أي خطأ , وقيمة غير صفرية إذا كانت الأمور علي ما يرام , ولهذا كان شرطنا ببساطة , علي الصورة :
While (infile)
وهي صيغة أبسط بكل تأكيد , ولكنها قد لا تكون واضحة المعني لغير المتخصصين .
إدخال وإخراج المحارف
يمكن استخدام منتميتين إلي ostream, istream , وهما put(), get() لكتابة محرف وحيد وقراءته , وفي البرنامج التالي سوف نستخدم الدالة put() لقراءة عبارة نصية باعتبارها مصفوفة محارف .
ochar.cpp
// ochar.cpp
// file output with characters
#include <fstream.h> // for file functions
#include <string.h> // for strlen()
#include <conio.h>
void main()
{
char str[] = "Time is a great teacher, but unfortunately "
"it kills all its pupils. Berlioz";
ofstream outfile("TEST.TXT"); // create file for output
for(int j=0; j<strlen(str); j++) // for each character,
outfile.put(str[j]); // write it to file
getche();
}
حددنا في البرنامج طول المصفوفة بما تعيده الدالة المسئولة عن تحديد طول العبارات النصية , وهي كما مر بك strlen() . والبرنامج التالي يظهر العبارة علي الشاشة .
ichar.cpp
// ichar.cpp
// file input with characters
#include <fstream.h> // for file functions
#include <conio.h>
void main()
{
char ch; // character to read
ifstream infile("TEST.TXT"); // create file for input
while( infile ) // read until EOF
{
infile.get(ch); // read character
cout << ch; // display it
}
getche();
}
يستخدم البرنامج الدالة get() التي تستمر في القراءة إلي أن تصل إلي نهاية الملف وكل محرف يقرأ يظهر علي الشاشة .
وهناك أسلوب أخر لقراءه الملفات المحتوية علي محارف , باستخدام الدالة rdbuf() ] اختصار read buffer [ , وهي تنتمي إلي الفئة ios وهذه الدالة تعيد مؤشرا للكائن المصاحب لكائن التدفق , والذي يحتوي علي الذاكرة المرحلية المخزنة بها المصفوفة والبرنامج التالي يستعمل هذه الدالة .
ichar2.cpp
// ichar2.cpp
// file input with characters
#include <fstream.h> // for file functions
#include <conio.h>
void main()
{
ifstream infile("TEST.TXT"); // create file for input
cout << infile.rdbuf(); // send its buffer to cout
getche();
}
وهذا البرنامج له نفس نتيجة البرنامج السابق , ويتميز بأنه أصغر من برنامج يمكن كتابته للتعامل مع الملفات . والدالة المذكورة تعرف ماذا تعيده عند مقابلة نهاية الملف .
تعليقات
إرسال تعليق