آموزش کار با فایل در C++
یکی از قابلیت های زبان برنامه نویسی C++ کار کردن با فایل ها می باشد. سی پلاس پلاس در این زمینه بسیار سریع است و به همین خاطر مورد استفاده قرار می گیرد. کار کردن با فایل ها گرچه ساده است اما دارای جزئیاتی است که در طی این پست به آن خواهیم پرداخت.
فایل چیست ؟
فایل غالبا به مجموعه ای از اطلاعات گفته می شود که بر روی یک حافظه ذخیره می شود. با ذخیره کردن این اطلاعات می توان در دفعات بعدی از آن استفاده نمود. به طور کلی کار کردن با یک فایل دارای ۳ مرحله است :
- فایل باید باز / ساخته شود.
- اطلاعات خوانده / نوشته شود.
- فایل بسته شود.
گرچه مرحله سوم اجباری نیست اما توصیه می شود این کار حتما انجام شود.
کار با فایل در C++
برای کار با فایل ها در C++ از کتابخانه fstream استفاده می شود. این کتابخانه امکان خواندن و نوشتن را فراهم می کند و به دلیل داشتن عضوهای تابعی متعدد بسیار کاربردی است. برنامه زیر یک فایل را باز می کند :
#include <iostream> #include <fstream> using namespace std; int main() { ifstream inputFile; inputFile.open("info.txt"); return 0; }
در قطعه کد بالا، ابتدا کتابخانه fstream به برنامه اضافه گردیده است، سپس یک شی از روی آن به نام inputFile ساخته شده و با استفاده از عضو تابعی open فایل info.txt را باز کرده است. اینکه این فایل به درستی باز شده است و یا اینکه با خطا مواجه شده است موضوعی است که در ادامه به آن خواهیم پرداخت.
آشنایی با پرچم ها
کار کردن با فایل ها روش های مختلفی دارد به همین دلیل تعدادی پرچم (flag) طراحی شده است تا کار آسان تر شود. در جدول زیر تعدادی از پرکاربردترین پرچم ها به همراه توضیحات آنها دیده می شود :
توضیحات | نام پرچم |
اگر فایل موجود باشد، برنامه مستقیما به انتهای فایل می رود اما عملیات نوشتن ممکن است در هرجایی از فایل اتفاق بیافتد. | ios::ate |
وقتی یک فایل به صورت دودویی باز شود خواندن از آن و یا نوشتن در آن نیز به صورت دودویی انجام می شود. | ios::binary |
اطلاعات از فایل خوانده می شود. اگر فایل موجود نباشد در این حالت ساخته نمی شود و عملیات باز کردن فایل با خطا مواجه می شود. | ios::in |
اگر فایلی موجود نباشد این پرچم از ساختن فایل جلوگیری می کند. | ios::nocreate |
اگر فایلی از قبل موجود باشد تابع باز کردن فایل با خطا مواجه می شود. | ios::replace |
اطلاعات درون فایل نوشته می شود و به صورت پیش فرض محتوای قبلی موجود در فایل (در صورت وجود) پاک می شود. | ios::out |
اگر فایل موجود باشد محتوای آن پاک می شود. | ios::trunc |
اگر فایل موجود باشد، محتوای آن باقی می ماند و عملیات نوشتن از انتهای فایل صورت می گیرد. | ios::app |
در ادامه کار با نحوه استفاده از این پرچم ها آشنا خواهیم شد.
ساختن شی از روی fstream
از دو روش می توانیم برای ساختن شی از روی fstream استفاده کنیم. در روش اول مقداردهی ها همزمان با ساخته شدن شی انجام می شود و در روش دوم ابتدا شی ساخته شده و سپس مقداردهی ها صورت می گیرد.
روش اول:
fstream myFile("names.dat", ios::in | ios::out);
در کد فوق شی myFile برای کار با فایلی تحت نام names.dat با پرچم های ios::in و ios::out ساخته شده است.
روش دوم:
fstream myFile; myFile.open("names.dat", ios::in | ios::out);
در این روش ابتدا شی ساخته می شود و سپس با استفاده از عضو تابعی open مقداردهی های اولیه انجام می شود.
آیا جریان به درستی ایجاد شده است ؟
بعد از اینکه یک فایل را باز کردیم ابتدا باید بررسی کنیم که باز کردن فایل به درستی انجام شده است یا خیر. اگر این مرحله انجام نشود ممکن است در ادامه به مشکلاتی برخورد کنیم که خطایابی آن دشوار باشد. برای بررسی این موضوع از دو روش مختلف می توان استفاده کرد.
روش اول – دستور شرطی if:
fstream myFile; myFile.open("names.dat", ios::in); if (!myFile) cout << "Error !";
در این برنامه، مقدار Error! چاپ خواهد شد زیرا این فایل به طور پیش فرض در سیستم من وجود ندارد و همانطور که در جدول بالا توضیح داده شد؛ پرچم ios::in در صورتی که فایل موجود نداشته باشد آن را نمی سازد و عملیات با مشکل مواجه خواهد شد.
روش دوم – استفاده از عضو تابعی fail:
fstream myFile; myFile.open("names.dat", ios::in); if (myFile.fail()) cout << "Error !";
در کد بالا به جای علامت تعجب از متد fail استفاده شده است و خروجی برنامه در عمل تفاوتی با روش اول نخواهد داشت اما به دلیل خوانایی بیشتر توصیه می شود از روش دوم استفاده شود.
عملیات خواندن و نوشتن
برای خواندن و نوشتن، به ترتیب از دو عملگر extraction و insertion استفاده می شود. اگر دانش ابتدایی در زبان سی پلاس پلاس داشته باشید حتما از این عملگرها برای چاپ یک داده با استفاده از دستور cout استفاده کرده اید. برای اینکه عملیات خواندن و نوشتن را به خوبی فرا بگیریم از دو مسئله کاربردی استفاده می کنیم.
مسئله اول: آرایه ای متشکل از ۱۰ عدد اعشاری که بیانگر شعاع دایره های مختلف است به شما داده شده است. برنامه ای بنویسید که مساحت دایره های حاصل از این شعاع ها را محاسبه کرده و در یک فایل تحت عنوان result.txt ذخیره کند.
#include <iostream> #include <fstream> #include <iomanip> using namespace std; int main() { fstream fileObj; fileObj.open("result.txt", ios::out); // اگر فایل موجود نباشد به طور پیش فرض ساخته می شود if (fileObj.fail()) { cout << "failed"; return 1; } float info[10] = { 4.32, 19.7, 4.0, 5.0, 6.0, 12.2, 24.1, 8.3, 9.9, 0.4 }; float pi = 3.14159265; fileObj << setw(5) << "R" << setw(10) << "Area" << endl; for (int i = 0; i < 10; i++) fileObj << setw(5) << info[i] << setw(10) << pi * info[i] * info[i] << endl << endl; fileObj.close(); return 0; }
ابتدا یک شی از روی fstream به نام fileObj ساخته شده است. در خط بعدی فایلی تحت نام result.txt باز شده است. لازم به ذکر است اگر این فایل وجود نداشته باشد با این دستور به صورت خودکار ایجاد می شود. سپس از فلگ ios::out استفاده کردیم که بدین معناست می خواهیم اطلاعات را بر روی فایل بنویسیم. در خطوط ۱۱ الی ۱۵ بررسی می شود که عملیات ایجاد stream به درستی انجام شده است یا خیر. در خطوط ۱۷ و ۱۸ در متغیر ابتدایی تحت نام های info و pi تعریف گردیده است. متغیر info شامل اطلاعات مربوط به شعاع دایره ها می باشد که توسط سوال به ما داده شده است. همچنین pi حاوی مقداری تقریبی برای عدد پی است. در خطوط بعدی مشاهده می شود که چگونه با insertion operator عملیات نوشتن در فایل انجام می شود. در نهایت نیز با دستور close، جریان بسته می شود.
خروجی حاصل از این برنامه را در تصویر زیر مشاهده می کنید:
مسئله دوم: یک فایل متنی به نام names.txt موجود است. هر خط از این فایل حاوی نام و نام خانوادگی یک دانشجو است. برنامه ای بنویسید که اطلاعات موجود در این فایل متنی را خوانده و در خروجی چاپ کند.
#include <iostream> #include <fstream> using namespace std; int main() { fstream fileObj; fileObj.open("names.txt", ios::in); char str[80]; while (!fileObj.eof()) { fileObj.getline(str, 81); cout << str << endl; } return 0; }
در اینجا از دو عضو تابعی کتابخانه fstream استفاده کردیم. تابع eof که سرواژه End Of File می باشد و با استفاده از آن می توان مشخص کرد که آیا برنامه به انتهای فایل رسیده است یا خیر. دستور دیگر getline است که برای خواندن خط به خط از فایل مورد استفاده قرار می گیرد. تابع getline دارای دو ورودی است، پارامتر اول یک اشاره گر از نوع کاراکتر با طول مشخص است و پارامتر دوم تعداد کاراکتری که از فایل خوانده می شود. شاید این سوال برایتان پیش بیاید که چرا مستقیما از extraction operator استفاده نکردیم ؟ چون در این روش هنگامی که اشاره گر درون فایل به whitespace می رسد آن را نادیده می گیرد و به جای آن از کاراکتر newline استفاده می کند. همین موضوع باعث می شود که یک خط که دارای فاصله میان کلمات است به درستی نمایش داده نشود. خروجی این برنامه را در شکل زیر می بینیم :
سخن پایانی
کار با فایل ها در سی پلاس پلاس دارای جزئیات بسیاری است. در این مقاله سعی شد شما به طور اصولی با نحوه ی کار با فایل ها آشنا شوید. برای اطلاعات بیشتر می توانید از منابع اصلی این زبان استفاده کنید.