هر برنامه ورودی هایی را دریافت می کند و پس از انجام محاسبات و پردازش هایی، خروجی را بر روی نمایشگر نشان می دهد و یا درون یک فایل و یا درون پایگاه داده ذخیره می کند. همچنین چندین روش برای دریافت ورودی وجود دارد. برنامه می تواند از طریق خط فرمان یا واسط های گرافیکی ورودی بگیرد و یا اینکه از درون یک فایل ورودی را بخواند.

دریافت ورودی و چاپ خروجی

فایل واحد منطقی ذخیره سازی داده ها بر روی دیسک است که توسط سیستم عامل مدیریت و نگهداری می شوند. سیستم فایل (File System) واحدی از (هسته) سیستم عامل است که وظیفه نگهداری، مدیریت، دسترسی و تغییرات فایل ها را بر عهده دارد.

فایل ها انواع مختلفی دارند ولی می توان آنها را به دو دسته فایل های متنی (Text Files) و فایل های باینری (Binary Files) دسته بندی کرد. اما در سیستم عامل های یونیکسی و شبه یونیکسی می توان فایل ها را به صورت دیگری دسته بندی کرد.

فایل های متنی که هر فرمتی را شامل می شوند. این فایل ها می توانند فایل های متنی، فایل های دودویی، فایل های HTML و فایل های با فرمت XML یا فایل های با فرمت CSV باشند. دایرکتوری ها خود نوع دیگری از فایل ها هستند. برخی از فایل ها را فایل های ویژه (Special Files) می گونید که شامل موارد زیر می شوند.

  • فایل های Pipe.
  • فایل های سوکت های یونیکسی (Unix Socket Files).
  • فایل های ابزارهای بلاکی (Block Devices) مانند فایل های مربوط به دیسک ها.
  • فایل های ابزارهای کاراکتری (Character Devices) مانند فایل های مربوط به ماوس.

این مطلب اشاره به فایل های متنی اسکی (ASCII) دارد. در پایتون برای خواندن از فایل ها و نوشتن بر روی فایل ها نیازی به ماژول یا کتابخانه مجزا نیست و خواندن از و نوشتن بر روی فایل ها بسیار ساده است.

برای خواندن از فایل یا نوشتن بر روی فایل، باید در ابتدا آن فایل را باز (Open) کنیم. در پایتون برای این کار از تابع درونی ()open استفاده می کنیم. این تابع دو آرگومان ورودی دریافت می کند، آرگومان اول مسیر و نام فایل و آرگومان دوم حالتی (Mode) است که فایل برای آن باز خواهد شد.

در سیستم عامل های یونیکسی و شبه یونیکسی مانند لینوکس، مکینتاش، سولاریس و FreeBSD زمانی که یک فایل را برای خواندن یا نوشتن باز می کنیم، سیستم عامل یک عدد صحیح مثبت و منحصر به فرد را به آن اختصاص می دهد که آنرا File Descriptor می گویند. در واقع این عدد به فایل باز شده اشاره می کند.

سه File Descriptor پیشفرض که File Descriptor های استاندارد (Standard File Descriptors) نیز نامیده می شوند اعداد 0، 1 و 2 را به صورت پیشفرض دریافت کرده اند. عدد صفر معرف Stdin یا Standard Input، عدد 1 معرف Stdout یا Standard Output و عدد 2 معرف Stderr یا Standard Error است. پس زمانی که توسط تابع ()open یک فایل را باز می کنید عدد 3 به عنوان File Descriptor به آن اعطا می شود و الی آخر.

mode آرگومان اخیتاری است که به حالتی که فایل با آن باز می شود اشاره می کند. اگر مقداری برای این پارامتر انتخاب نشود، به صورت پیشفرض حالت r (حالت read) در نظر گرفته می شود. حالت دیگر w است که فایل را برای نوشتن (write) باز می کند. اگر فایل وجود نداشته باشد و آنرا برای خوانده شدن باز کنید، یک خطا نشان داده خواهد شد. ولی اگر فایلی را که وجود ندارد برای نوشتن باز کنید خطایی نشان داده نمی شود و یک فایل همنام ایجاد می شود. در قطعه کد ۲ فایل sample.txt وجود ندارد و چون آنرا برای خواندن باز کردیم، خطا نشان داده می شود.

همانطور که گفته شد اگر آرگومان mode را مشخص نکنید، حالت r در نظر گرفته می شود. در قطعه کد ۳ فایل sample.txt را برای نوشتن باز می کنیم و چون این فایل وجود ندارد، فایل مورد نظر در مسیر مشخص شده ایجاد خواهد شد.

هر زمان که به صورت موفقیت آمیز فایلی را برای خواندن یا نوشته شدن باز کنید، یک شی یا نمونه از نوع file ایجاد می شود که در قطعه کد ۳ آنرا file_object نامگذاری کرده ایم. فرض کنید یک فایل که چندین خط در آن وجود دارد را برای نوشتن یا خواندن و نوشتن باز کرده اید. رفتار حالت w به این صورت است که محتوای جدید را به طور کامل بر روی محتوای قبلی می نویسد. به عبارتی محتوای قبلی از دست خواهد رفت. در مقابل حالت w، حالت a وجود دارد که محتوای جدید را در انتهای محتوای قبلی اضافه می کند. a به معنی append کردن محتوای جدید به انتهای محتوای قبلی است.

حالت دیگر استفاده از علامت + است. اگر فایل را فقط برای نوشتن باز کنید، پس نمی توانید آنرت بخوانید و اگر فایلی را فقط برای خواندن باز کنید، پس نمی توانید روی آن بنویسید. با استفاده از ترکیب +r می توانید به صورت همزمان فایل را برای خواندن و نوشته شدن باز کنید. برای این کار مطابق قطعه کد ۴ عمل کنید. در قطعه کد ۴ بازهم اگر فایل وجود نداشته باشد، فایل در دایرکتوری مشخص شده، ایجاد خواهد شد.

حالت دیگر خواندن و نوشتن بر روی فایل های دودویی است. فایل هایی مانند تصاویر، ویدیوها و فایل های اجرایی (Executable Files) از انواع فایل های دودویی هستند. برای خواندن و نوشتن بر روی آنها از حالت r+b استفاده کنید.

بنابراین به طور خلاصه می توان انواع حالت های باز کردن و خواندن از فایل ها را به صورت زیر خلاصه و دسته بندی کرد:

  • برای خواندن فایل باید از r استفاده کنید.
  • برای خواندن و نوشتن بر روی فایل باید از +r استفاده کنید.
  • برای نوشتن محتوای جدید در انتهای محتوای قبلی (Append کردن) باید از a استفاده کنید.
  • برای نوشتن محتوای جدید بر روی محتوای فعلی باید از w استفاده کنید.

پیدا کردن عدد File Descriptor فایل باز شده

از طریق شی file و استفاده از متد ()fileno می توان عدد اختصاص داده شده به فایل را پیدا کرد. این عدد از ۳ شروع می شود. به طور مثال در قطعه کد ۷ یک شی file به نام fd ایجاد شده و سپس عدد File Descriptor آن توسط متد ()fileno نشان داده شده است و در نهایت توسط متد ()close فایل را بسته ایم.

همچنین توسط name و mode به صورت قطعه کد ۸ می توان نام فایل باز شده و حالتی که فایل با آن باز شده را پیدا کنیم.

از طریق closed می توان بررسی کرد که‌ آیا فایل بسته شده است یا نه. به طور مثال در قطعه کد ۹ بررسی می شود که ‌آیا فایل بسته شده است یا نه. در صورتی که فایل باز باشد پیغام مناسب نشان داده می شود.

خواندن از فایل ها توسط تابع ()read

فرض کنید فایلی داریم که محتوای آن به صورت زیر است. فایل شامل ۱۵ خط است که چندین خط در آن خالی هستند.

برای خواندن از فایل ها باید حالت را r قرار دهیم. پس از باز شدن موفق فایل، از طریق شی file و با استفاده از متدهای ()read و ()readline و ()readlines می توان محتوای فایل را بخوانیم. متد ()read اندازه (Size) بایت هایی را هر بار می تواند بخواند را دریافت می کند. به طور مثال اگر عدد یک را دریافت کند، یک بایت اول را می خواند و اگر عدد ۱۰ را دریافت کند، ۱۰ بایت اول را می خواند.

اگر چندین بار متد ()read را پشت هم فراخوانی کنیم، در مرتبه اول به اندازه مشخص شده می خواند و در مرتبه بعد از ادامه مرتبه قبلی می خواند. به طور مثال فرض کنید که در مرتبه اول عدد ۵ را به تابع ارسال کرده اید، بنابراین از بایت اول تا پنجم را می خواند و متد ()read دوم از بایت ششم به بعد را می خواند.

در خط اول نام و مسیر فایل در یک متفیر ذخیره شده است. در خط سوم و درون بدنه try، توسط تابع ()open فایل درون متغیر file_name در حالت read باز می شود و یک شی file به نام fd ایجاد می شود. در خطوط ۴ و ۵ به ترتیب ۵ بایت و ۹ بایت از فایل خوانده می شوند. خط اول فایل شامل رشته This is line1 است که اولین ()read پنج بایت اول یعنی This و ()read دوم ۹ بایت آخر را می خواند. اگر مقداری را به عنوان اندازه به متد ()read ارسال نکنیم، تابع read از اولین تا آخرین بایت را می خواند و در خروجی نمایش می دهد. در واقع کل فایل خوانده می شود.

اگر قطعه کد ۱۲ را به صورت قطعه کد ۱۳ بازنویسی کنیم که توسط یک حلقه for و متد ()read محتوای فایل را بخوانیم، در هر خط از خروجی یک کاراکتر نشان داده می شود.

خواندن یک خط از فایل توسط تابع ()readline

زمانی خواندن یک خط از فایل به انتها می رسد که کاراکتر newline که در انتهای خط آمده باشد. در قطعه کد ۱۶ توسط حلقه for خط به خط فایل درون لیست lines ذخیره می شود. در انتهای هر خط کاراکتر n\ قرار دارد که به معنی انتهای فایل و رفتن به خط جدید برای شروع خط جدید است.

تابع ()readline هر بار کل یک خط از فایل را می خواند. بنابراین اگر یکبار تابع را فراخوانی کنیم، اولین خط از فایل خوانده می شود و اگر دو مرتبه تابع را فراخوانی کنیم، دو خط اول فایل خوانده خواهد شد. در قطعه کد ۱۷ دو خط اول از فایل خوانده می شود.

همانطور گه گفته شد کاراکتر n\ نشان دهنده پایان خط است و اگر قطعه ک ۱۷ را به صورت زیر (قطعه کد ۱۷) باز نویسی کنیم، خواهید دید که کاراکتر n\ نیز در انتهای هر رشته که در لیست lines ذخیره می شود، وجود دارد. هر رشته در این لیست نشان دهنده یک خط از فایل است.

برای جدا کردن کاراکتر n\ باید از تابع ()split به صورت قطعه کد ۱۹ استفاده کنیم. خروجی قطعه کد ۱۹ لیستی از لیست ها است که هر لیست درونی آن از دو عضو تشکیل شده است. عضو اول خود خط است و عضو دوم یک رشته خالی است.

باز کردن فایل با استفاده از عبارت with … as

از عبارت with می توانیم برای باز کردن یک فایل استفاده کنیم. در قطعه کد ۲۰ و ۲۱ از عبارت with … as  فایل باز شده است و تعداد خطوط خالی شمارش می شود و از یک لیست برای ذخیره سازی محتوای خطوط غیر خالی استفاده می شود. خطوط خالی با کاراکتر n\ نشان معین می شوند.