فایلها و مدیریت خطا
خواندن و نوشتن فایل در پایتون: open، حالتهای w، a، r و close
همهٔ متغیرهایی که تا حالا ساختهایم در حافظهٔ موقت (RAM) بودند؛ برنامه که تمام شود، از بین میروند. برای اینکه کارمان ماندگار شود (اسامی ثبتنامشده، نتایج یک محاسبه، تنظیمات برنامه)، باید آن را در فایل ذخیره کنیم.
هر سیستمعامل روش خودش را برای مدیریت فایلها دارد. پایتون یک روش مستقل از سیستمعامل فراهم میکند که با یک دستگیرهٔ فایل (file handle) کار میکند.
باز کردن فایل
name_handle = open("test.txt", "w")
- اولین آرگومان نام فایل است (اگر فقط اسم بدهید، کنار برنامه ساخته میشود).
- دومین آرگومان حالت (mode) است:
"w"یعنی برای نوشتن (write) باز کن. openیک دستگیره برمیگرداند که از این به بعد با آن با فایل کار میکنیم.
| حالت | معنی | اگر فایل وجود نداشته باشد | اگر وجود داشته باشد |
|---|---|---|---|
"r" |
خواندن (read)، پیشفرض | خطای FileNotFoundError |
از اول خوانده میشود |
"w" |
نوشتن (write) | ساخته میشود | ⚠️ محتوای قبلی کامل پاک میشود |
"a" |
افزودن (append) | ساخته میشود | به انتهای آن اضافه میشود |
💡 برای فایلهای غیرمتنی (عکس، PDF، …) یک
b(binary) اضافه میشود:"rb"،"wb". حالتهای ترکیبی مثل"r+"هم وجود دارند، ولی فعلاً به همین سه حالت نیاز داریم.
نوشتن
h = open("salam.txt", "w")
h.write("ali" + "\n")
h.write("reza")
h.close()
print(open("salam.txt").read())
writeفقط رشته میپذیرد. برای نوشتن عدد، اول تبدیلش کنید:h.write(str(42)).- برخلاف
print، تابعwriteخودش به خط بعد نمیرود. اگر خط جدید میخواهید، خودتان کاراکتر"\n"(«برو خط بعد!») را اضافه کنید.
نوشتن ورودیهای کاربر
name_handle = open("names.txt", "w")
for i in range(3):
name = input("Enter name: ")
name_handle.write(name + "\n")
name_handle.close()
print(open("names.txt").read())
افزودن به انتهای فایل
اگر همان کد بالا را دوباره با "w" اجرا کنید، اسامی قبلی پاک میشوند. برای اضافه کردن به آخر فایل از "a" استفاده کنید:
h = open("log.txt", "a")
h.write("یک خط جدید\n")
h.close()
print(open("log.txt").read())
چند بار پشت سر هم اجرا کنید و ببینید فایل بزرگتر میشود.
خواندن
# اول یک فایل بسازیم
h = open("test.txt", "w")
h.write("Sara\nAli\nReza\n")
h.close()
# حالا بخوانیم
name_handle = open("test.txt", "r")
for line in name_handle:
print(line)
name_handle.close()
با for روی دستگیرهٔ فایل، خط به خط فایل را میخوانیم.
به خروجی دقت کنید: بین اسمها یک خط خالی آمده! چرا؟ چون هر line خودش یک \n در انتها دارد ("Sara\n") و print هم یک \n دیگر اضافه میکند. راهحلها را در درس بعد میبینیم.
چرا close مهم است؟
وقتی فایلی را باز میکنید، سیستمعامل منبعی را برای برنامهٔ شما رزرو میکند. اگر نبندید:
- ممکن است آخرین چیزهایی که نوشتهاید هنوز روی دیسک ذخیره نشده باشند (پایتون نوشتن را برای سرعت بیشتر در حافظه جمع میکند و موقع
closeیکجا ذخیره میکند). - برنامههای دیگر (یا خود شما) ممکن است نتوانند فایل را باز کنند یا پاک کنند.
- سیستمعامل تعداد محدودی فایل باز را اجازه میدهد.
این باگ موذی است: روی کامپیوتر خودتان همهچیز درست کار میکند، ولی روی یک سرور که هر روز هزاران بار فایل باز میکند، بعد از چند روز یا چند هفته ناگهان همهچیز میخوابد، چون «یادمان رفته ببندیم». منابع انحصاری مثل وبکم، اتصال پایگاه داده یا قفل فایل از این هم حساسترند: تا یکی آزادشان نکند، بعدی نمیتواند از آنها استفاده کند.
به این کارهای «بعد از تمام شدن، مرتب کن» میگویند پاکسازی (cleanup). پایتون برایش ابزار مخصوصی دارد که دیگر هیچوقت لازم نیست close را به خاطر بسپارید: دستور with.
متن فارسی و encoding
اگر در فایل فارسی مینویسید، روی ویندوز ممکن است خطای UnicodeEncodeError بگیرید یا هنگام باز کردن فایل حروف درهمریخته ببینید. دلیلش این است که ویندوز بهطور پیشفرض از کدگذاری قدیمیتری استفاده میکند. راهحل: همیشه encoding را صریح بنویسید:
h = open("farsi.txt", "w", encoding="utf-8")
h.write("سلام دنیا\n")
h.close()
h = open("farsi.txt", "r", encoding="utf-8")
print(h.read())
h.close()
💡 عادت حرفهای: هر جا فایل متنی باز میکنید،
encoding="utf-8"بنویسید. ضرری ندارد و جلوی یک دستهٔ کامل از باگهای ویندوزی را میگیرد.
تمرین
برنامهای بنویسید که ۳ اسم از کاربر بگیرد و در فایل students.txt (هر اسم در یک خط) ذخیره کند. بعد فایل را بخواند و شمارهدار نمایش دهد:
1. Sara
2. Ali
3. Reza
جواب
h = open("students.txt", "w", encoding="utf-8")
for i in range(3):
h.write(input("name: ") + "\n")
h.close()
h = open("students.txt", "r", encoding="utf-8")
number = 1
for line in h:
print(f"{number}. {line.strip()}")
number += 1
h.close()
strip() آن \n آخر هر خط را حذف میکند. (درس متدهای رشته)