تدفقات عمليات الملفات في ++C

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();


   }





وهذا البرنامج له نفس نتيجة البرنامج السابق , ويتميز بأنه أصغر من برنامج يمكن كتابته للتعامل مع الملفات . والدالة المذكورة تعرف ماذا تعيده عند مقابلة نهاية الملف .

التسميات: