الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

سلسلة دروس في الfiles (الملفات)

رائج
بدأه أباسليمان في 31 أكتوبر 2009 · 6 رد · 22,564 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

بسم الله الرحمن الرحيم

هذه سلسلة من الدروس عن files سأضعها بين فترة وفترة . وأسأل الله أن ينفع بها.

الملفات

Files

الدرس الأول :

طبعا في هذا الفصل سنتعامل مع الملفات بشكل كلي وسنستعمل في ذالك مكتبة <fstream> .

إذا سألت أي مبرمج بالغ عاقل عن عمل هذا الكود


using namespace std;
int main()
{
cout<<"helo"<<endl;
}
 #include <iostream>

فسيجيبك وبكل تأكيد أن هذا البرنامج سيطبع على الشاشة كلمة helo)) . ولكن ماذا لو أردت أن اطبع كلمة (helo) على ملف بدلا من الشاشة .

في هذه الحالة عليك أن تتعرف على مكتبة (fstream) وبالتحديد على كلاس اسمه (ofstream) .

لا أطيل عليك انضر لهذا المثال البسيط :


using namespace std;
int main()
{
ofstream MyFile;
MyFile.open("HeloText.txt");
MyFile<<"Helo"<<endl;
MyFile.close();
}
 #include <fstream>

- الأن ما الكيفية التي يتم بها الكتابة على الملف ؟.

مثلها مثل ال(cout) بالضبط :

 MyFile<<"Helo"<<endl;

الكتابة على الملف لايختلف عن الcout إلا في اسم الكائن .

ولكن قبل الكتابة على الملف علينا أن نقوم بخطوات لإنشاء الملف وإعطاءه اسما وربطه مع البرنامج

وهذه الخطوات هي :

1- تضمين مكتبة ال fstream


using namespace std
 #include <fstream>

2- في ال main قمنا بإنشاء كائن (object) من ofstream طبعا حرف (O) يرمز إلى out put أي مخرج .

 ofstream MyFile;

3- ثم قمنا بعد ذالك بإستدعاء دالة ال(open) وهذه الدالة طبعا ضمن كلاس ofstream

 MyFile.open("HeloText.txt");

وهذه الدالة تستقبل بارمتر من نوع (char *) طبعا في البارمتر قمنا بإعطاء اسم لملفنا وكذالك حددنا نوعه وتستطيع أن تكتب أي نوع تريد .

هذه الدلة وضيفتها انشاء ملف أنت تقوم بإعطائها اسم الملف وكذالك تقوم الدالة بربط البرنامج مع الملف .

ثم أخيرا نبدأ بالكتابة على الملف .

ولكن علينا بعد أن ننتهي من الكتابة على الملف علينا أن نقطع الرابطة التي بين البرنامج والملف .

وذالك بإستعمال دالة close()

 MyFile.close();

طبعا هذه الخطوات الأساسية ولكن هناك خطوات أخرى تعمل لتحسين البرنامج مثل التأكد من انشاء أو فتح الملف وغيرها من الدوال .

سنكتب برنامج صغيرا الأن شبيها ببرنامج المفكرة . حيث يأخذ من المستخدم كتاباته ويخزنها في ملف من نوع (txt) .

طبعا سنأخذ من المستخدم البيانات عبرة دالة cin ومن ثم سنخزن البيانات إلى الملف.

وسنستعمل دالة fail() وذالك لتحقق من إمكانية فتح أو انشاء الملف .


#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
string data;
ofstream MyFile;
MyFile.open("nodpad.txt");
if(MyFile.fail())
{
cout<<"ERROR"<<endl;
exit(1);
}
cout<<"write What in your mind : "<<endl;
do
{
cin>>data;
MyFile<<data<<" ";
}while(data!="exit");
MyFile.close();
}
 #include <fstream>

سأبدأ بالشرح من هنا :


{
cout<<"ERROR"<<endl;
exit(1);
}
 if(MyFile.fail())

هنا استخدمنا دالة fail() التي تحدثت عنه سابقا حيث تقوم هذه الدالة بالتحقق من أن انشاء الملف قد تم بلا مشاكل ثم سترجع false أما في حالة أن الملف لم ينشئ (لعدم وجود مساحة في الذاكرة مثلا ) فسوف يرجع true .

طبعا إذا ارجعت الدالة true فسوف يتحقق الشرط وسيضهر رسالة ERROR وسيخرج من البرنامج .

أما إذا ارجعت false فسوف يكمل البرنامج وسيصل إلى هنا :


do
{
cin>>data;
MyFile<<data<<" ";
}while(data!="exit");
 cout<<"write What in your mind : "<<endl;

أتوقع أن هذه الفقرة واضحة ولاتحتاج إلى شرح حيث سيأخذ بيانات من المستخدم وسيكتبها في الملف .

تمرين : حاول أن تنشئ برنامج يأخذ من المستخدم معلومات عن جنسيته , عمره , الجنس, الحالة الإجتماعية

ثم يقوم البرنامج بحفظ هذه البيانات في ملف .

الدرس الثاني سيكون عن :

- كيفية قراءة البيانات من ملف .

- كيفية البحث في الملف والتعديل عليه

- برنامج لتسجيل اسماء الموظفين وبياناتهم ورواتبهم .

4
#2

- القراءة من الملف :

كما كنت تستطيع أن تكتب على الملف فإنك تستطيع كذالك أن تقرأ من ملف وبنفس المكتبة ولكن بإستعمال كائن أخر وهو ifstream .

سنأخذ الأن مثال بسيطا وهو مرتبط بالمثال الأول (الذي يحفظ كلمة hello في ملف ) حيث سنقوم بأخذ كلمة hello من الملف ونخزنها في متغير من نوع string ثم سنقوم بطباعة المتغير على الشاشة عبر دالة الcout .


#include <fstream>
#include <iostream>
using namespace std;
int main()
{
string Example;
ifstream MyFile;
MyFile.open("HeloText.txt");
if(MyFile.fail())
{
cout<<"EROOR"<<endl;
exit(7);
}
MyFile>>Example;
cout<<Example<<endl;
MyFile.close();
}

سأشرح من بعد دالة الmain .


ifstream MyFile;

هنا أنشأنا كائن من نوع ifstream وهذا الكلاس مختص بقراءة البيانات من الملف عكس ال ofstream الذي يختص بإدخال البيانات (طباعة البيانات) إلى الملف. طبعا i اختصار ل input .


MyFile.open("HeloText.txt");

هنا استدعاء لدالة open وهي تقريبا نفس ال(open) في (ofstream) والفرق بينها وبين ال open التي في ال ofstream أنها تقوم بالبحث عن اسم الملف المعطى لها وفتحه بينما ال(open) في ال ofstream تقوم بإنشاء الملف .


if(MyFile.fail())
{
cout<<"EROOR"<<endl;
exit(7);
}


هنا استدعينا دالة ال(fail) والتي بدورها ستتحقق من فتح الملف وترجع false في حالة أن الملف تم فتحه و true في حالة أن الملف لم يفتح ويخرج من البرنامج (مثال: إذ لم يكن الملف موجودا أصلا) .


MyFile>>Example;

وهذا هو بيت القصيد ومن هذا الأمر ندخل البيانات من الملف إلى المتغير Example لعلك لاحظت أن الكائن يشبه

دالة ال (cin) من ناحية ادخال البيانات وهي تشبها كذالك من ناحية المبدأ في إدخال البيانات . حيث أن الكائن (MyFile) سيدخل البيانات إلى المتغير (Example) حتى يجد أمامه فراغ أو سطر جديد . يعني مثلا لو كان في الملف اسم ما وليكن ( ( Khalid Alhussayenسيقوم البرنامج في هذه الحالة بتخزين كلمة (Khalid) وسيتجاهل اسم العائلة (Alhussayen) إذ أنه يتبع الية عمل دالة (cin) تماما ولكي نعالج هذه المشكلة يجب أن نستعمل دالة (getline) .

- خاصية (ios::app) التابعة لدالة ال(open)

- البحث والتعديل على البيانات

طريقة الكتابة على الملف التقليدية تحدث بعض المشاكل مثال : إذا اردت أن تحفظ بعض البيانات الجديدة في ملف معين فإن البيانات القديمة سوف تمحى وسيكتب عليها ماذا لو أردت أن تبقى هذه البيانات القديمة وتكتب البيانات الجديدة بعدها مباشرة , كذالك طريقة تخزين البيانات يجب أن تكون مدروسة إذا اردنا أن نسترد البيانات من الملف.

في هذه الفقرة سوف نأخذ بعض الخصائص في الكتابة على الملف لحل بعض المشاكل . ولكن قبل ذالك سأتكلم عن تعديل البيانات لنضرب مثالا عليه وفي هذا المثال ستبرز لنا بعض المشاكل في الكتابة على الملف التي تكلمنا عنها .

التعديل على الملف لها أكثر من طريقة , طبعا هي ليست أوامر برمجية معينة بل تستطيع أن تقول عنها أنها خوارزميات وتختلف من شخص لأخر ومن برنامج لأخر.

سنأخذ الأن مثال شامل للملفات وسنعتمد على ال (OOP) في كتابة البرنامج .

البرنامج سيكون عن اسماء الموظفين ورواتبهم .

طبعا قمت بفصل الواجهة ولكني لم أفصل الملفات عن بعضها .

ملاحظة : البرنامج سئ من ناحية التطبيق العملي ولكنه جيد من ناحية الشرح وذالك لأنه يحتوي على بعض المشاكل التي سوف نحلها .

وإليك واجهة البرنامج (inter face) :


#include <iostream>
#include <fstream>
using namespace std;
class em
{
float Salary;
string Name;
public:
em();
void setSalary(float);
void setName(string);
float getSalary();
string getName();
void saveToFile();
};

كما تشاهد البرنامج بسيط لا يحتوي إلا على اسم الموظف وراتبه . ودوال الset و get وكذالك دالة save و load .

طبعا سأشرح دالتا ال save و load فقط .

من الإسم تستطيع أن تخمن عمل دالة save وهو تخزين البيانات وكذالك load استيراد البيانات.

وهذا هو الملف الكود المصدري للكلاس


em::em()
{
Salary=0;
Name=" ";
}
void em::setSalary(float s)
{
Salary=s;
}
void em::setName(string n)
{
Name=n;
}

float em::getSalary()
{
return Salary;
}
string em::getName()
{
return Name;
}
void em::saveToFile()
{
ofstream save;
save.open("info.txt");
if(save.fail())
{
cout<<"ERROR"<<endl;
exit(7);
}
save<<Name<<"\t"<<Salary<<endl;
save.close();
}
void loadToMemory(em le[],int size)
{
ifstream load;
float sa;
string na;
load.open("info.txt");
if(load.fail())
{
cout<<"EROOR : Cannot open File"<<endl;
exit(7);
}
for(int i=0;i<size;i++)
{
load>>na>>sa;
le.setName(na);
le.setSalary(sa);
}
}

لاحظ أن دالة البناء تعطي قيم ابتدائية للمتغيرات (فراغ للأسم و صفر لراتب) وذالك لأننا سنحفظ هذه المتغيرات في ملف فلا يصح أن نحفظ قيما عشوائية .

لاحظ أيضا طريقة التخزين في دالة ال(save) حيث يقوم البرنامج بتخزين الإسم ثم فراغات(سبع فراغات تقريبا) ثم يحقظ الراتب وبعد ذالك سينتقل إلى سطر جديد .

س- لماذ قمنا بذالك ؟.

قلنا قبل ذالك أن تخزين البيانات مثله مثل ال (cin) اي أنه سيأخذ البيانات إلا أت يجد فراغا أو سطر جديد.

كذالك لاحظ أن دالة ال(save) جعلناها ضمن الكلاس وأستطيع أن اريح عمري وأجعله مثل دالة ال(load) ولكن أردت أن اضعها هكذا حتى تتضح المشكلة التي ستبرز لنا بعد كتابة الmain .

انضر لل main


int main()
{
int const size=3;
em e[size];
float sa;
string na;
int choice;
cout<<"1- enter information"<<endl<<"2- modification"<<endl;
cin>>choice;
switch(choice)
{
case 1:
for(int i=0;i<size;i++)
{
cout<<"enter the name : ";
cin>>na;
cout<<"enter the salary : ";
cin>>sa;
e.setName(na);
e.setSalary(sa);
e.saveToFile();
}
break;
case 2:
loadToMemory(e,size);
for(int i=0;i<size;i++)
{
cout<<e.getName()<<"\t"<<e.getSalary()<<endl;
}

e[2].setName("mhmed");
cout<<e[2].getName()<<"\t"<<e[2].getSalary();
for(int i=0;i<2;i++)
e.saveToFile();
break;
}
}


كما أخبرتك من قبل هناك مشكلة ما في البرنامج وتحديد في دالة ال(save)(حاول أن تخمن أين المشكلة)

لنحلل البرنامج :

لاحظ أننا بعد أن انتهينا من تعريف المتغيرات أننا طلبنا من المستخدم إدخال 1- في حالة أنه أراد أن يدخل البيانات

و 2- في حالة أنه اراد أن يعدل على البيانات .

بعد ذالك استخدمنا دالة ال(switch) ففي حالة كان الإختيار 1 فسوف يدخل البرنامج دالة التكرار (for) وسيطلب من المستخدم معلومات حو اسم الموظف وراتبه.

وبعد ذالك يقوم البرنامج بإسناد القيم عبر دالة ال(set) ومن ثم يستدعي دالة ال(save) .

تقوم دالة ال(save) بدورها فتنشئ ملف وتخزن به البيانات وإلا الأن لامشكلة .

س- إذا اين تحدث المشكلة ؟.

تحدث المشكلة بعد ذالك مباشرة فدالة التكرار ستقوم بدورها إلا أن يتحقق الشرط . فسوف يقوم البرنامج بتكرار الخطوات وسوف تستدعي دالة ال(save) مرة أخرى دالة ال(save) ستقوم بإنشاء ملف ولكن الملف موجود مسبقا لذا سيقوم البرنامج بالكتابة على الملف ومسح البيانات القديمة .

س- إذا ما الحل ؟.

يكون الحل بالتعديل على دالة (save) وبالتحديد على دالة ال (open) وإضافة بارمتر.


save.open("info.txt",ios::app);

لاحظ أن البارمتر المضاف هو (ios::app) .

هذا يغير قليلا من وظيفة دالة ال (open) فتصبح بدل أن تنشئ ملف وتكتب عليه سوف تتأكد قبل أن تنشئ ملف إن كان هناك ملف بنفس الإسم فإن كان هناك ملف بنفس الإسم فسوف تقوم بالكتابة من أخر الملف أي أنه لن يمحي أي بيانات بل سيتقوم بالكتابة بعد أخر بيانات .

قم بالتعديل على الكود أعلاه وستجد أن المشكلة قد انحلت .

بالنسبة للحالة رقم 2 فلن اشرحه أعتقد أنه واضح إذا أننا نقوم بالتعديل على البيانات . ثم نعود لحفظ البيانات.

أعود وأكرر بقولي أن هذا البرنامج سئ جدا من ناحية التطبيق العملي ولكنه جيد من ناحية الشرح

- دالة ال(eof())

- البحث في البيانات

لكي تقوم بالبحث عن بيانات معينة (اسم شخص مثلا) فإننا يجب أن نمر على الملف من البداية إلى النهاية ونقارن كل اسم مع الإسم الذي نريد أن نبحث عنه اي أننا سنستخدم دالة الشرط ولكن كيف نعرف اننا وصلنا إلى نهاية الملف؟

أقول نستطيع أن نعرف ذالك وذالك عبر دالة ال(eof()) وهي اختصار لجملة (end of file) هذه الدالة مهمتها أن ترجع (true) في حالة أنها وصلت إلى نهاية الملف و (false) في حالة أنها لم تصل إلى نهاية الملف .

سنقوم الأن بكتابة دالة بسيطة اسمها (search) هذه الدالة تستقبل بارمتر من نوع string وتبحث عنه في الملف فإذا وجدته فسوف تطبع الإسم والراتب , طبعا هذه الدالة سأرفقها بالبرنامج الذي كتبناه سابقا .


void search(string name)
{
ifstream load("info.txt");
float sa;
string na;
if(load.fail())
{
cout<<"EROOR : Cannot open File"<<endl;
exit(7);
}
while(!load.eof())
{
load>>na>>sa;


if(na==name)
{
cout<<na<<"\t"<<sa;
load.close();
return;
}
}
cout<<"Sorry data not found .";
load.close();


طبعا معضمه مكرر ماعدا دالة التكرار while لاحظ أن الشرط هو (!load.eof()) لماذا ؟ لإن دالة الeof())) ترجع false في حالة أن الملف لم يصل إلى النهاية ونحن نريد أن يدخل ال while ماداما دالة (eof()) ترجع false . وتخرج من التكرار إذا ارجعتtrue اي أنها وصلت إلى نهاية الملف .

طبعا وضعنا دالة الشرط if لنتحقق من وجود الإسم فإذا وجدنا الإسم في الملف فسيقوم البرنامج بطباعة الإسم والراتب ثم استعملنا return طبعا معروف أن الدالة الفارغة (void) لاترجع شيئا ولكن حين تضع return فقط فسوف تخرج من الدالة ولن تكمل مابعد الدالة .أما في حالة أننا لم نجد الإسم فسوف تضهر رسالة اعتذار .

انتهي الفصل بحمد من الله تذكر أن هذا الفصل مجرد اساس ولم نتعمق في تفاصيل هذه المكتبة إن اتقنت هذا الفصل فأنت بإذن الله تكون قد اتقنت اساس هذا الفصل وبإستطاعتك التعمق فيه والبحث في الإنترنت لتجد الكثير من الدروس والأمثلة .

تم تعديل هذه المشاركة بواسطة أباسليمان في 29 نوفمبر 2009 في 23:49

1
#3

شكرا جزيلا . لقد استفدت من المقال جداً .

تحياتي

OMANI FOR EVER

-------------------------------------------------

post-12787-12780191801974.jpg

-------------------------------------------------

#4

مقالة جيدة جدا ، أخي ، جزاك الله خير .

أرشحها كمقال ضمن فهرس المنتدى .

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#6

أشكركم جميعا على الإطراء .

وأصل هذا المقال فصل ظمن مشروع كتاب (مازال تحت الإنشاء ) لنادي فريق البرمجة .

أسال الله أن ينفع به الإسلام والمسلمين.

#7

أخي الكريم عندي سؤال يخص التعامل مع الملفات

في بعض الأحيان أريد أن أدخل معلومات من ملفين منفصلين مثلا :

ifstream input;
input.open("aaa.txt");

//input and processing the data

input.close()



input.open("bbb.txt");

//input and processing the data

input.close()

ولكن لا يعمل الكود نهائيا فأضطر لاستخدام كائن إدخال لكل ملف أريد التعامل معه

هل هذه هي الطريقة الصحيحة أم أنا أخطأت في شئ ؟

مواضيع مشابهة