فصل ۶: فایل‌ها و خطاها — pathlib، UTF-8، CSV، JSON و logging

open، encoding="utf-8" و with: فایل متنی فارسی بدون خرابی

مهم‌ترین آرگومانی که همه فراموش می‌کنند

فایل روی دیسک فقط بایت است. برای تبدیل بایت به متن (و برعکس) باید کدگذاری (encoding) مشخص شود. اگر آن را ننویسید، پایتون از کدگذاری پیش‌فرض سیستم‌عامل استفاده می‌کند؛ در لینوکس و مک UTF-8 است، اما در ویندوز با تنظیمات منطقه‌ای فارسی معمولاً cp1256 (کدگذاری قدیمی عربی ویندوز). نتیجه: کدی که روی لپ‌تاپ شما درست کار می‌کند روی سرور لینوکسی فارسی را خراب می‌کند یا UnicodeDecodeError می‌دهد. قاعده‌ی بی‌استثنا:

هر open متنی، encoding="utf-8" صریح دارد.

from pathlib import Path

notes = Path("notes.txt")

with open(notes, "w", encoding="utf-8") as f:
    f.write("سفارش KSH-101 ثبت شد\n")
    print("ارسال به کاشان", file=f)       # print هم می‌تواند در فایل بنویسد

with open(notes, encoding="utf-8") as f:
    for line_no, line in enumerate(f, start=1):
        print(line_no, line.rstrip("\n"))

with: بستن تضمینی

فایل باز منبع سیستم‌عامل است و باید بسته شود. بلوک with تضمین می‌کند فایل در پایان بلوک بسته شود، حتی اگر وسط کار خطا رخ دهد. بدون with، اگر خطایی بین open و close پیش بیاید، فایل باز می‌ماند؛ در ویندوز فایل باز قفل است و نه می‌توانید حذفش کنید، نه اکسل می‌تواند آن را بنویسد.

حالت‌های باز کردن

حالتمعنیاگر فایل وجود داشته باشد
"r"خواندن (پیش‌فرض)باز می‌شود؛ اگر نباشد FileNotFoundError
"w"نوشتنمحتوا بدون هشدار پاک می‌شود
"a"افزودن به انتهاحفظ می‌شود؛ متن به انتها اضافه می‌شود
"x"ساخت انحصاریFileExistsError؛ جلوی بازنویسی تصادفی را می‌گیرد
"rb" / "wb"باینری (تصویر، PDF)بدون encoding؛ با bytes کار می‌کند

خواندن فایل بزرگ

f.read() کل فایل را یک‌جا در حافظه می‌آورد. برای فایل لاگ چند گیگابایتی، روی خود شیء فایل حلقه بزنید (مثل مثال بالا)؛ پایتون خط‌به‌خط می‌خواند و حافظه ثابت می‌ماند.

وقتی کدگذاری فایل را نمی‌دانید

فایلی که از یک نرم‌افزار حسابداری قدیمی یا Notepad ویندوز ۷ آمده، احتمالاً cp1256 است. اول با UTF-8 امتحان کنید و اگر خطا داد، با encoding="cp1256" بخوانید و خروجی را UTF-8 ذخیره کنید. پارامتر errors="replace" به‌جای خطا، کاراکترهای خراب را با � جایگزین می‌کند؛ برای «یک نگاه سریع» خوب است، برای داده‌ی واقعی نه.

نکته‌هایی که کمتر کسی می‌داند

  • اجرای پایتون با py -X utf8 script.py یا تنظیم متغیر محیطی PYTHONUTF8=1 پیش‌فرض همه‌ی open ها را UTF-8 می‌کند؛ طبق PEP 686 این رفتار در نسخه‌های آینده‌ی پایتون پیش‌فرض می‌شود.
  • py -X warn_default_encoding script.py هر open بدون encoding را با هشدار EncodingWarning به شما نشان می‌دهد؛ روش سریع پیدا کردن همه‌ی جاهای خطرناک.
  • پایتون در حالت متنی \r\n ویندوزی را هنگام خواندن به \n تبدیل می‌کند و هنگام نوشتن در ویندوز برعکس؛ اگر فایل برای لینوکس است، newline="\n" بدهید.
  • f.write خط جدید اضافه نمی‌کند؛ print(..., file=f) اضافه می‌کند. writelines هم با وجود نامش خط جدید نمی‌گذارد.
  • بعد از open(..., "w") حتی اگر هیچ چیزی ننویسید، فایل قبلی خالی شده است؛ برای ذخیره‌ی امن، در فایل موقت بنویسید و بعد با Path.replace جایگزین کنید.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.