I/O مخفف input/output و به معنی ورودی/خروجی است. در برنامه نویسی اوقاتی پیش می آید که کدهای ما باید با فایل های دیگری صحبت کنند و اینطور نیست که فقط در یک فایل ساده باشیم. به طور مثال شاید کدهای ما باید یک صفحه وب را دانلود کنند، شاید کدهای ما باید یک فایل روی هارد دیسک را بخوانند، شاید در حال صحبت با یک سیستم دیگر و یک پایگاه داده دیگر باشیم و الی آخر. یکی از رایج ترین مثال های موجود برای I/O خواندن یا ویرایش فایل ها است.
زبان پایتون تابع پیش فرضی دارد که به ما اجازه می دهد فایل ها را باز کرده و بخوانیم. نام این تابع open است. من برای انجام این کار یک فایل ساده متنی به نام roxo.txt را ایجاد می کنم و درون آن جمله Learn Development at Roxo.ir/plus را قرار می دهم. در مرحله بعدی آن را بدین شکل باز می کنم:
my_file = open('roxo.txt') print(my_file)
همانطور که می بینید من در نهایت متغیر my_file را print کرده ام. به نظر شما با این کار چه اتفاقی می افتد؟ من با اجرای این کدها نتیجه زیر را می گیرم:
<_io.TextIOWrapper name='roxo.txt' mode='r' encoding='UTF-8'>
یک شیء به نام TextIOWrapper دریافت کرده ایم، یک name دریافت کرده ایم که نام فایل ما است، یک mode دریافت کرده ایم که در ادامه آن را توضیح خواهم داد و در نهایت encoding فایل را نیز دریافت کرده ایم که UTF-8 می باشد. دیگر خصوصیت های ذکر شده کاملا واضح هستند و نیازی به توضیح ندارند بنابراین قبل از ادامه بحث باید خصوصیت mode را بررسی کنیم. mode می تواند یکی از حالت های زیر را داشته باشد:
در واقع ساختار کامل تابع open به شکل زیر است:
open(filename, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
در صورتی که filename (نام فایل) پاس داده شده وجود نداشته باشد خطای FileNotFoundError را دریافت می کنید. پایتون برای خواندن اطلاعات سه تابع read و readline و readlines را در اختیار ما می گذارد. بگذارید با ساده ترین حالت یعنی read آشنا شویم:
my_file = open('roxo.txt') print(my_file.read())
با اجرای کد بالا نتیجه زیر را می گیریم:
Learn Development at Roxo.ir/plus
بنابراین به همین سادگی موفق به خواندن محتویات این فایل شده ایم. بیایید محتویات فایل roxo.txt را کمی ویرایش کنیم:
Learn Development at Roxo.ir/plus
This is the second line
and here's the third
حالا اگر دوباره متد read را صدا بزنیم (کد قبلی را اجرا کنیم) چه می شود؟
Learn Development at Roxo.ir/plus
This is the second line
and here's the third
هر سه خط را دریافت می کنیم. بنابراین متوجه می شویم که read کل محتویات فایل را یکجا برمی گرداند بنابراین در هنگام کار با فایل های بسیار بزرگ مناسب نیست چرا که مقدار زیادی ram اشغال می کند اما متد readline چطور؟
Learn Development at Roxo.ir/plus
همانطور که مشخص است readline فقط خط اول را برایمان برمی گرداند. البته می توانید تعداد کاراکترهای مورد نظرتان را به این متد پاس بدهید تا فقط کاراکترهای خاصی را مشاهده کنیم:
my_file = open('roxo.txt') print(my_file.readline(20))
با اجرای کد بالا نتیجه زیر را می گیریم:
Learn Development at
یعنی فقط ۲۰ کاراکتر اول برایمان برگردانده شده است. در ضمن اگر این متد را پشت سر هم صدا بزنید می توانیم به خطوط بعدی برویم:
my_file = open('roxo.txt') print(my_file.readline()) print(my_file.readline()) print(my_file.readline()) print(my_file.readline()) print(my_file.readline())
نتیجه:
Learn Development at Roxo.ir/plus
This is the second line
and here's the third
در مرحله بعدی متد readlines را داریم:
my_file = open('roxo.txt') print(my_file.readlines())
با اجرای این کد نتیجه زیر را دریافت می کنیم:
['Learn Development at Roxo.ir/plus\n', '\n', 'This is the second line\n', '\n', "and here's the third\n"]
به عبارت دیگر تمام خط ها به صورت اعضای یک لیست برایمان برگردانده شده اند!
در انتها زمانی که کارمان با فایل تمام شده است باید فایل را به صورت دستی ببندیم در غیر این صورت فایل در کدها باز خواهد ماند! به همین دلیل باید با استفاده از دستور close این کار را انجام بدهیم:
my_file = open('roxo.txt') print(my_file.readlines()) my_file.close()
documentation رسمی پایتون هشدار داده است که در عملیات هایی مانند write (نوشتن در فایل) باید حتما فایل را close کنید، در غیر این صورت احتمال این وجود دارد که داده ها به صورت کامل در فایل نوشته نشوند.
روشی که بالاتر یاد گرفتیم کمی آزار دهنده است چرا که در انتها باید فایل را به صورت دستی می بستیم تا با خطاهای عجیب یا رفتار های غیر منتظره روبرو نشویم اما روش بسیار بهتری برای باز کرن فایل ها و خواندن اطلاعاتشان وجود دارد؛ دستور with open. برای آشنایی با این دستور باید یک بار فایل خودمان را با آن باز کنیم:
with open('roxo.txt') as my_file: print(my_file.readlines())
دستور with open آدرس و نام فایل را از شما می گیرد (roxo.txt) و سپس کلیدواژه as را آورده و یک نام دلخواه را به فایل خود می دهید. من در اینجا نام my_file را انتخاب کرده ام. دستور with open دقیقا مانند یک شرط if دارای فروفتگی است و بلوک مخصوص خودش را دارد بنابراین باید دستور print خود را با فرورفتگی بنویسید. با اجرای دستور بالا نتیجه زیر را می گیرید:
['This is a new line from python code!his is the second line\n', '\n', "and here's the third\n"]
بنابراین نتیجه نیز مانند قبل بوده و تفاوتی نمی کند. تا زمانی که درون این بلوک باشید می توانید با فایل خود کار کنید و زمانی که به انتهای آن برسید فایل به صورت خودکار بسته می شود و نیازی به بستن آن به صورت دستی نیست. معمولا زمانی که بحث از کار با فایل ها در پایتون می شود، این روش استاندارد ترین روش است و اکثر برنامه نویسان پایتون از آن استفاده می کنند. ما تا این قسمت فقط فایل ها را خوانده ایم، بیایید کمی در فایل ها بنویسیم. اگر از بخش mode ها یادتان باشد برای این کار باید از حالت +r استفاده کنیم تا قابلیت خواندن و نوشتن در فایل را داشته باشیم:
with open('roxo.txt', mode="r+") as my_file: text = my_file.write("This is a new line from python code!") print(text)
من در اینجا برای نوشتن در این فایل از متد write استفاده کرده ام و نتیجه اش را درون متغیری به نام text قرار داده ام. در نهایت نیز آن را چاپ کرده ام تا بدانیم متد write چه چیزی را برمی گرداند. با اجرای کد بالا عدد ۳۶ را دریافت می کنیم! آیا می توانید حدس بزنید این عدد چیست؟ این عدد تعداد کاراکتر هایی است که درون فایل نوشته شده است. اگر به رشته پاس داده شده به write در کد بالا توجه کنید، خواهید دید که تعداد کاراکتر هایش ۳۶ عدد می باشد. حالا اگر به فایل roxo.txt نگاه کنیم، جمله جدید اضافه شده را در آن پیدا خواهیم کرد:
This is a new line from python code!
This is the second line
and here's the third
آیا شما هم متوجه رفتار عجیب پایتون شدید؟ محتوای این فایل قبل از اجرای این دستور به شکل زیر بود:
Learn Development at Roxo.ir/plus
This is the second line
and here's the third
از همین نتیجه متوجه می شویم که پایتون cursor یا نشانگر خود را در ابتدای فایل قرار داده است. cursor یعنی نقطه ای که پایتون از آنجا شروع به نوشتن می کند و زمانی که آن را در ابتدای فایل قرار بدهد یعنی از ابتدای فایل شروع به نوشتن خواهد کرد. شاید با دیدن نتیجه بالا تصور کنید که متد write متن ما را جایگزین خط اول می کند اما اینطور نیست. برای اثبات این موضوع به شما راه حلی دارم! ابتدا به فایل roxo.txt بروید و محتوای آن را به طور کامل پاک کنید. در مرحله بعد متن زیر را در آن قرار داده و فایل را ذخیره کنید:
Learn Development
This is the second line
and here's the third
در مرحله بعد دوباره کد قبلی خود را اجرا کنید:
with open('roxo.txt', mode="r+") as my_file: text = my_file.write("This is a new line from python code!") print(text)
حالا پس از اجرای کد دوباره به فایل roxo.txt نگاهی می اندازیم:
This is a new line from python code!d line
and here's the third
آیا نتیجه برایتان تعجب آور بود؟ همانطور که می بینید خط اول به طور کامل حذف شده است، خط دوم تا حرف d (کلمه second) حذف شده است و سپس بقیه فایل را داریم! برای زبان پایتون خطوط جداگانه اهمیتی ندارد بلکه پایتون و دیگر زبان های برنامه نویسی همه چیز را در قالب کاراکتر ها می بینند. حتی رفتن به خط بعد یک کاراکتر است (n\) که به آن line break می گوییم. به زبان ساده تر از نظر پایتون محتوای فایل ما بدین شکل بوده است:
Learn Development\nThis is the second line\nand here's the third
بنابراین پایتون همه چیز را در یک خط می بیند و شروع به نوشتن در فایل می کند. ما ۳۶ کاراکتر را به آن پاس داده ایم بنابراین ۳۶ کاراکتر از ابتدای فایل ما حذف کرده و ۳۶ کاراکتر جدید را جایگزین آن می کند! در چنین موقعیتی سوالی مطرح می شود: چطور می توانیم داده های قبلی خود را حفظ کنیم؟ با استفاده از حالت append یا ضمیمه! ما در این مثال دیدیم که cursor در ابتدای فایل قرار می گرفت بنابراین از اتبدای فایل شروع به نوشتن می کرد. از طرفی من در بخش mode ها برایتان توضیح دادم که باز کردن فایل در حالت append باعث قرار گرفتن cursor در انتهای فایل می شود بنابراین به جای اینکه متون قبلی را حذف کنیم، به آن ها اضافه می کنیم. برای تست حالت append ابتدا به فایل roxo.txt رفته و باز هم متن آن را به شکل زیر تغییر بدهید:
Learn Development
This is the second line
and here's the third
حالا کدهای خود را به شکل زیر ویرایش می کنیم:
with open('roxo.txt', mode="a") as my_file: text = my_file.write("This is a new line from python code!") print(text)
توجه کنید که من mode را روی a (مخفف append) گذاشته ام. با اجرای کد بالا نتیجه زیر را دریافت می کنیم:
Learn Development
This is the second line
and here's the thirdThis is a new line from python code!
همانطور که می بینید هیچ قسمتی از متن قبلی ما حذف نشده است، بلکه رشته جدید ما به انتهای متن اضافه شده است. چرا؟ به دلیل اینکه پایتون همه چیز را در به شکل یک خط می بیند. در صورتی که بخواهیم متن ما به خط جدیدی اضافه شود دو راه وجود دارد:
راه اول: باید کاراکتر line break را نیز به رشته خود پاس بدهیم:
with open('roxo.txt', mode="a") as my_file: text = my_file.write("\nThis is a new line from python code!") print(text)
کاراکتر های n\ و r\ برای مشخص کردن انتهای خط در سیستم عامل های مختلف (لینوکس، ویندوز، iOs و غیره) استفاده می شوند. از آنجایی که من با لینوکس کار می کنم، از یک n\ استفاده کرده ام. با اجرای این کد محتویات roxo.txt به شکل زیر نمایش داده خواهد شد:
Learn Development
This is the second line
and here's the third
This is a new line from python code!
در صورتی که می خواهیم نوشته ما با یک خط فاصله باشد باید دو بار n\ را قرار بدهیم:
with open('roxo.txt', mode="a") as my_file: text = my_file.write("\n\nThis is a new line from python code!") print(text)
متن roxo.txt را به حالت اول برگردانید و سپس دوباره کد بالا را اجرا کنید. توجه کنید که در این کد دو کاراکتر line break داریم. نتیجه:
Learn Development
This is the second line
and here's the third
This is a new line from python code!
راه دوم: به فایل roxo.txt رفته و یک یا دو خط خالی را در انتهای آن قرار بدهید:
Learn Development
This is the second line
and here's the third
در مرحله بعدی کد زیر را می نویسیم:
with open('roxo.txt', mode="a") as my_file: text = my_file.write("This is a new line from python code!") print(text)
با اجرای این کد محتوای فایل roxo.txt به شکل زیر تغییر پیدا می کند:
Learn Development
This is the second line
and here's the third
This is a new line from python code!
چرا؟ به دلیل اینکه من در انتهای فایل دو n\ داشتم بنابراین cursor در آخر فایل قرار می گیرد (پس از n\ ها) بنابراین در حالت عادی چیزی وجود ندارد که بخواهد overwrite شود.
لازم به ذکر است که در حالت append و write، اگر فایل پاس داده شده وجود نداشته باشد، یک فایل جدید برایمان ساخته می شود.
در این قسمت می خواهم به صورت خلاصه در رابطه با مسیر دهی در زبان پایتون صحبت کنم. تا این قسمت از این آموزش ما همیشه فایل roxo.txt را در کنار فایل پایتون خودمان (test.py) داشته ایم اما اگر اینطور نبود چه؟
تصور کنید که فایل roxo.txt در یک پوشه دیگر به نام text files باشد. در این حالت چطور می توانیم درون کدهایمان به این فایل آدرس بدهیم؟ به کد زیر توجه کنید:
with open('roxo.txt', mode="r") as my_file: text = my_file.read() print(text)
به نظر شما این کد پاسخگو خواهد بود؟ من mode را روی r (خواندن) گذاشته ام تا اگر فایل را پیدا نکردیم حتما خطا دریافت کنیم. اگر mode روی a یا w باشد، خطایی نمی گیریم بلکه فایل جدیدی را خواهیم ساخت. همانطور که قبلا هم توضیح دادم برای اجرای کدهایتان چند راه وجود دارد. یا اینکه vscode را در پوشه مورد نظر باز کنید و سپس با کلیک روی run کد را اجرا نمایید یا اینکه ترمینال را در پوشه مورد نظر باز کرده و دستور زیر را اجرا نمایید:
python3 test.py
من از همین روش (روش دوم) برای اجرای این کدها استفاده می کنم:
Traceback (most recent call last): File "test.py", line 1, in <module> with open('roxo.txt', mode="r") as my_file: FileNotFoundError: [Errno 2] No such file or directory: 'roxo.txt'
از آنجایی که فایل roxo.txt را به درون پوشه ای دیگر انتقال دادم خطا گرفته ایم. برای حل این مشکل باید آدرس دهی را به شکل زیر انجام بدهیم:
with open('./text files/roxo.txt', mode="r") as my_file: text = my_file.read() print(text)
به عبارتی نام پوشه را نیز ذکر کرده ام. در ضمن علامت /. در ابتدای آدرس به معنی آدرس دهی نسبی (relative path) می باشد. یعنی چه؟ یعنی آدرس دهی از همین پوشه شروع می شود؛ یعنی از همین پوشه به دنبال پوشه ای به نام text files بگرد که درون خود فایلی به نام roxo.txt داشته باشد. از آنجایی که به صورت پیش فرض از آدرس دهی نسبی استفاده می شود، نیازی به آوردن آن نداشتیم و در کدهای قبلی به جای roxo.txt/. از roxo.txt استفاده می کردیم بنابراین بدانید که هر دو حالت به آدرس دهی نسبی اشاره می کنند.
در مقابل آدرس نسبی (relative path) آدرس مطلق (absolute path) را داریم که در آن آدرس کامل فایل را ذکر می کنیم:
with open('/home/amir/Desktop/Roxo Academy/Python/text files/roxo.txt', mode="r") as my_file: text = my_file.read() print(text)
این آدرس کامل فایل در سیستم من است. من از لینوکس استفاده می کنم بنابراین آدرس کامل فایل به شکل بالا در آمده است اما اگر از ویندوز استفاده می کنید باید آدرس کامل خود را به آن بدهید. به طور مثال:
C:/user/amir/roxo/python/text files/roxo.txt
در هر دو حالت کد ما بدون مشکل اجرا خواهد شد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.