کتابخانه‌ی استاندارد · پایه

ورودی خروجی io

خواندن از صفحه‌کلید، خواندن و نوشتن پرونده و نوشتن در خروجی خطا.

واردسازی ورودی خروجی

نام آرگومان‌ها شناسه‌ی خود کد است و معادل فارسی ندارد؛ گونه‌ها، کلیدواژه‌ها و نام بسته‌ها با معادل فارسی نوشته می‌شوند.

بیشتر برنامه‌ها باید چیزی بخوانند و چیزی بنویسند. بسته‌ی ورودی خروجی ساده‌ترین راه‌ها را برای این کار دارد: یک خط از کاربر بخوانید، یک پرونده را یک‌جا بخوانید یا بنویسید، یا با یک پرونده‌ی باز تکه‌تکه کار کنید.

تابع‌هایی که نام پرونده می‌گیرند (مثل خواندن پرونده) خودشان پرونده را باز می‌کنند و می‌بندند. تابع‌هایی که پرونده* می‌گیرند با پرونده‌ای کار می‌کنند که خودتان با سیستم عامل.بازکردن باز کرده‌اید؛ یادتان باشد آن را با بستن ببندید.

برای کارهای روزمره از ورودی خروجی استفاده کنید. برای این‌که بدانید مسیری وجود دارد یا نه، فهرست پوشه‌ها، پاک کردن یا تغییر نام پرونده به سیستم پرونده بروید؛ برای ساختن مسیر به مسیر و برای خروجی قالب‌بندی‌شده به قالب بندی.

چند نکته که معمولاً غافلگیر می‌کند:

  • نبودن پرونده این‌جا خطا حساب نمی‌شود. خواندن پرونده رشته‌ی تهی برمی‌گرداند و خط ها وکتوری با یک رشته‌ی تهی؛ پس پرونده‌ی خالی و پرونده‌ی ناموجود یک‌جور دیده می‌شوند. اگر فرقشان برایتان مهم است، اول با سیستم عامل.وجود دارد بررسی کنید.
  • خط‌ها فقط روی \n جدا می‌شوند. در پرونده‌ای که با پایان خط ویندوزی ذخیره شده، ته هر خطی که خط ها یا خواندن خط برمی‌گرداند یک \r می‌ماند؛ پیش از مقایسه، خط را پیرایش کنید.
  • اندازه‌ها بر حسب بایت است، نه حرف. هر حرف فارسی در UTF-8 دو بایت جا می‌گیرد، پس اگر پرونده‌ی فارسی را تکه‌تکه می‌خوانید، ممکن است یک حرف وسط دو تکه نصف شود.
  • تابع‌های متنی با رشته کار می‌کنند و رشته در نخستین بایت صفر تمام می‌شود. پرونده‌های دودویی مثل تصویر همان‌جا بریده می‌شوند؛ پس این بسته را برای متن نگه دارید.

خواندن پرونده‌ی تنظیمات

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

io-guide-config.salam
واردسازی ورودی خروجی
واردسازی رشته

تابع ریشه:
    ورودی خروجی.نوشتن پرونده("app.conf"، "# تنظیمات سرور\nدرگاه = ۸۰۸۰\n\nمیزبان = localhost\nکارگرها = ۴\n")
    ناپایا تنظیمات := نگاشت {} برگردان نگاشت<رشته، رشته>
    دیرکن تنظیمات.آزادکن()
    خط‌ها := ورودی خروجی.خط ها("app.conf")
    دیرکن خط‌ها.آزادکن()
    هر خط در خط‌ها:
        اگر خط == "" یا رشته.شروع با(خط، "#"):
            گذر
        پایان
        جای مساوی := رشته.اندیس(خط، "=")
        اگر جای مساوی < ۰:
            ورودی خروجی.نوشتن خطا با خط جدید("خط بدون '=' کنار گذاشته شد: " + خط)
            گذر
        پایان
        کلید := رشته.پیرایش(رشته.زیررشته(خط، ۰، جای مساوی))
        مقدار := رشته.پیرایش(رشته.زیررشته(خط، جای مساوی + ۱، خط.طول() - جای مساوی - ۱))
        تنظیمات.درج(کلید، مقدار)
    پایان
    سرچاپ "میزبان:"، تنظیمات.بگیر("میزبان")
    سرچاپ "درگاه:"، تنظیمات.بگیر("درگاه").به صحیح() + ۱
    سرچاپ "تنظیم‌های خوانده‌شده:"، تنظیمات.اندازه()
پایان
خروجیمیزبان: localhost درگاه: 8081 تنظیم‌های خوانده‌شده: 3

همه‌ی مقدارها به شکل متن برمی‌گردند؛ آن‌ها را همان‌جا که لازم دارید تبدیل کنید، این‌جا با به صحیح که رقم‌های فارسی را هم می‌فهمد. خط ها یک وکتور تازه می‌سازد، پس همان لحظه با دیرکن آزادش کنید. برای قالب‌های تودرتو، به‌جای تجزیه‌ی دستی سراغ encoding.json یا config بروید.

افزودن به پرونده‌ی گزارش

افزودن به پرونده در هر بار صدا زدن، پرونده را باز می‌کند، متن را به انتهایش می‌افزاید و دوباره می‌بندد؛ پس اگر برنامه وسط کار هم بایستد، گزارش روی دیسک کامل است. برای همین برای گزارش کار و ردگیری رویدادها مناسب است. نوشتن پرونده و افزودن به پرونده هر دو وقتی پرونده باز نشود منفی یک برمی‌گردانند، و وقتی مسیر از کاربر یا از تنظیمات می‌آید بهتر است این را بررسی کنید.

io-guide-log.salam
واردسازی ورودی خروجی

تابع ثبت(مسیر: رشته، سطح: رشته، پیام: رشته):
    اگر ورودی خروجی.افزودن به پرونده(مسیر، "[" + سطح + "] " + پیام + "\n") < ۰:
        ورودی خروجی.نوشتن خطا با خط جدید("نوشتن در " + مسیر + " ممکن نیست")
    پایان
پایان

تابع ریشه:
    مسیر := "app.log"
    ورودی خروجی.نوشتن پرونده(مسیر، "")
    ثبت(مسیر، "آگاهی"، "سرور راه افتاد")
    ثبت(مسیر، "هشدار"، "دیسک ۹۱ درصد پر است")
    ثبت(مسیر، "آگاهی"، "به درخواست پاسخ داده شد")
    ثبت(مسیر، "خطا"، "پایگاه داده پاسخ نداد")
    ناپایا مشکل‌ها := ۰
    خط‌ها := ورودی خروجی.خط ها(مسیر)
    دیرکن خط‌ها.آزادکن()
    هر خط در خط‌ها:
        اگر خط.شروع با("[هشدار]") یا خط.شروع با("[خطا]"):
            مشکل‌ها++
            سرچاپ خط
        پایان
    پایان
    سرچاپ خط‌ها.طول()، "خط،"، مشکل‌ها، "مورد نیاز به بررسی دارد"
پایان
خروجی[هشدار] دیسک ۹۱ درصد پر است [خطا] پایگاه داده پاسخ نداد 4 خط، 2 مورد نیاز به بررسی دارد

این‌جا نوشتن پرونده با یک رشته‌ی خالی، در هر اجرا گزارش را از نو شروع می‌کند. اگر آن خط را بردارید، گزارش از اجرایی به اجرای بعد بلندتر می‌شود.

خواندن پرونده‌ی بزرگ تکه‌تکه

خواندن پرونده و خط ها کل پرونده را در حافظه نگه می‌دارند. برای پرونده‌ای چندصد مگابایتی، خودتان آن را با سیستم عامل.بازکردن باز کنید و هر بار تعداد ثابتی بایت با خواندن بخوانید. رشته‌ی خالی یعنی به انتهای پرونده رسیده‌اید. پیش از کار با پرونده، آن را با پوچ مقایسه کنید: بازکردن این‌طور می‌گوید که پرونده باز نشد.

io-guide-chunks.salam
واردسازی ورودی خروجی
واردسازی سیستم عامل
واردسازی رشته

تابع ریشه:
    ورودی خروجی.نوشتن پرونده("big.txt"، رشته.تکرار("0123456789"، ۲۵))
    پ := سیستم عامل.بازکردن("big.txt"، "rb")
    اگر پ == پوچ:
        ورودی خروجی.نوشتن خطا با خط جدید("باز کردن big.txt ممکن نیست")
        برگشت ۱
    پایان
    دیرکن ورودی خروجی.بستن(پ)
    ناپایا جمع := ۰
    ناپایا تکه‌ها := ۰
    تا درست:
        تکه := ورودی خروجی.خواندن(پ، ۶۴ برگردان طبیعی۶۴)
        اگر تکه == "":
            بشکن
        پایان
        تکه‌ها++
        جمع += تکه.طول()
        سرچاپ "تکه‌ی"، تکه‌ها، "اندازه‌اش"، تکه.طول()، "بایت است"
    پایان
    سرچاپ جمع، "بایت در"، تکه‌ها، "تکه خوانده شد"
    برگشت ۰
پایان
خروجیتکه‌ی 1 اندازه‌اش 64 بایت است تکه‌ی 2 اندازه‌اش 64 بایت است تکه‌ی 3 اندازه‌اش 64 بایت است تکه‌ی 4 اندازه‌اش 58 بایت است 250 بایت در 4 تکه خوانده شد

اندازه بر حسب بایت است، پس مرز یک تکه ممکن است وسط یک حرف چندبایتی بیفتد؛ متن فارسی دقیقاً همین‌طور است. اگر فقط بایت می‌شمارید یا رونوشت می‌گیرید مهم نیست. اگر خط کامل لازم دارید، مثل بخش بعد با خواندن خط بخوانید.

پردازش پرونده خط به خط

خواندن خط هر بار یک خط را بدون پایان خط برمی‌گرداند، پس فقط همان خط در حافظه است. سرستون را یک بار بخوانید و بعد تا رسیدن به رشته‌ی خالیِ انتهای پرونده ادامه دهید.

io-guide-csv.salam
واردسازی ورودی خروجی
واردسازی سیستم عامل
واردسازی رشته

تابع ریشه:
    ورودی خروجی.نوشتن پرونده("sales.csv"، "کالا,تعداد,قیمت\nخودکار,3,12\nدفتر,2,45\nکیف,1,180\n")
    پ := سیستم عامل.بازکردن("sales.csv"، "rb")
    دیرکن ورودی خروجی.بستن(پ)
    سرستون := ورودی خروجی.خواندن خط(پ)
    سرچاپ "ستون‌ها:"، سرستون
    ناپایا جمع := ۰
    ناپایا خط := ورودی خروجی.خواندن خط(پ)
    تا خط != "":
        ستون‌ها := رشته.تفکیک کردن(خط، ",")
        هزینه := ستون‌ها.بگیر(۱).به صحیح() * ستون‌ها.بگیر(۲).به صحیح()
        سرچاپ ستون‌ها.بگیر(۰)، هزینه
        جمع += هزینه
        ستون‌ها.آزادکن()
        خط = ورودی خروجی.خواندن خط(پ)
    پایان
    سرچاپ "جمع کل:"، جمع
پایان
خروجیستون‌ها: کالا,تعداد,قیمت خودکار 36 دفتر 90 کیف 180 جمع کل: 306

یک نکته: خط خالیِ وسط پرونده هم به شکل رشته‌ی خالی برمی‌گردد و این حلقه همان‌جا می‌ایستد. اگر پرونده‌های شما خط خالی دارند، جای خواندن را با اندازه‌ی پرونده مقایسه کنید، یا اگر پرونده کوچک است با خط ها بخوانید. برای CSV واقعی با فیلدهای داخل گیومه، از encoding.csv استفاده کنید.

کدام تابع برای کدام کار

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

تابع‌ها

بخوان Input

تابع بخوان(): رشته
یک خط از ورودی استاندارد (معمولاً صفحه‌کلید) می‌خواند و بدون نویسه‌ی پایان خط برمی‌گرداند. همان کاری است که ورودی "" می‌کند. نمونه‌ی زیر منتظر تایپ شماست، پس اینجا فقط ترجمه می‌شود و اجرا نمی‌شود.
io-Input.salam
واردسازی ورودی خروجی

تابع ریشه:
    چاپ "نام شما چیست؟ "
    نام := ورودی خروجی.بخوان()
    سرچاپ "خوش آمدی،", نام
پایان

خواندن Read

تابع خواندن(f: پرونده*، size: طبیعی۶۴): رشته
حداکثر size بایت از جای فعلی پرونده می‌خواند. اگر به پایان پرونده رسیده باشید، رشته‌ی تهی برمی‌گرداند.
io-Read.salam
واردسازی ورودی خروجی
واردسازی سیستم عامل

تابع ریشه:
    ورودی خروجی.نوشتن پرونده("داده.txt"، "salam\ndonya\npayan\n")
    پ := سیستم عامل.بازکردن("داده.txt"، "rb")
    سرچاپ ورودی خروجی.خواندن خط(پ)
    سرچاپ ورودی خروجی.خواندن(پ، ۳ برگردان طبیعی۶۴)
    ورودی خروجی.جابجایی مکان نما(پ، ۰، ۰)
    چاپ ورودی خروجی.خواندن همه(پ)
    ورودی خروجی.بستن(پ)
پایان
خروجیsalam don salam donya payan

خواندن خط Readline

تابع خواندن خط(f: پرونده*): رشته
یک خط از پرونده می‌خواند، بدون \n آخرش (اگر پیش از آن \r باشد، می‌ماند). در پایان پرونده رشته‌ی تهی برمی‌گرداند و برای خط خالی هم همین را؛ پس حلقه‌ای که با "" می‌ایستد، سر نخستین خط خالی هم می‌ایستد.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-Read.salam

نوشتن Write

تابع نوشتن(f: پرونده*، data: رشته): صحیح
رشته را در پرونده‌ی باز می‌نویسد و تعداد بایت‌های نوشته‌شده را برمی‌گرداند؛ اگر دستگیره پوچ باشد، منفی یک.
io-Write.salam
واردسازی ورودی خروجی
واردسازی سیستم عامل

تابع ریشه:
    پ := سیستم عامل.بازکردن("گزارش.txt"، "wb")
    ن := ورودی خروجی.نوشتن(پ، "گزارش امروز\n")
    ورودی خروجی.بستن(پ)
    سرچاپ "بایت‌های نوشته‌شده:", ن
    مبدأ := سیستم عامل.بازکردن("گزارش.txt"، "rb")
    مقصد := سیستم عامل.بازکردن("رونوشت.txt"، "wb")
    سرچاپ "رونوشت:", ورودی خروجی.کپی(مقصد، مبدأ), "بایت"
    ورودی خروجی.بستن(مبدأ)
    ورودی خروجی.بستن(مقصد)
    چاپ ورودی خروجی.خواندن پرونده("رونوشت.txt")
پایان
خروجیبایت‌های نوشته‌شده: 22 رونوشت: 22 بایت گزارش امروز

جابجایی مکان نما Seek

تابع جابجایی مکان نما(f: پرونده*، offset: صحیح۶۴، whence: صحیح): صحیح
جای خواندن و نوشتن را در پرونده جابه‌جا می‌کند. offset فاصله بر حسب بایت است و whence می‌گوید از کجا شمرده شود: صفر یعنی از آغاز پرونده، یک یعنی از جای فعلی و دو یعنی از پایان پرونده. اگر موفق شود صفر و اگر نشود منفی یک برمی‌گرداند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-Read.salam

بستن Close

تابع بستن(f: پرونده*)
پرونده را می‌بندد. هر پرونده‌ای که باز می‌کنید باید یک بار بسته شود؛ جای خوبی برایش دیرکن است.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-Read.salam

خواندن پرونده ReadFile

تابع خواندن پرونده(path: رشته): رشته
همه‌ی محتوای یک پرونده را یک‌جا می‌خواند. اگر پرونده وجود نداشته باشد یا خواندنش ممکن نباشد، رشته‌ی تهی برمی‌گرداند.
io-ReadFile.salam
واردسازی ورودی خروجی

تابع ریشه:
    مسیر := "یادداشت.txt"
    ورودی خروجی.نوشتن پرونده(مسیر، "خط نخست\n")
    ورودی خروجی.افزودن به پرونده(مسیر، "خط دوم\n")
    چاپ ورودی خروجی.خواندن پرونده(مسیر)
    سرچاپ ورودی خروجی.خواندن پرونده("پرونده‌ای که نیست.txt") == ""
پایان
خروجیخط نخست خط دوم true

نوشتن پرونده WriteFile

تابع نوشتن پرونده(path: رشته، data: رشته): صحیح
پرونده را می‌سازد (یا اگر هست، محتوای قبلی‌اش را پاک می‌کند) و رشته را در آن می‌نویسد. تعداد بایت‌های نوشته‌شده را برمی‌گرداند و اگر نتواند پرونده را باز کند، منفی یک.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-ReadFile.salam

افزودن به پرونده AppendFile

تابع افزودن به پرونده(path: رشته، data: رشته): صحیح
رشته را به انتهای پرونده اضافه می‌کند و به محتوای قبلی دست نمی‌زند. اگر پرونده نباشد، ساخته می‌شود. مثل نوشتن پرونده تعداد بایت‌ها یا منفی یک برمی‌گرداند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-ReadFile.salam

خط ها Lines

تابع خط ها(path: رشته): وکتور<رشته>
پرونده را می‌خواند و خط‌هایش را در یک وکتور برمی‌گرداند. پیش از جدا کردن، کل متن پیرایش می‌شود؛ پس خط‌های خالی آخر پرونده و تورفتگی خط اول از بین می‌روند. پرونده‌ی ناموجود یک خط تهی می‌دهد. وکتور را پس از کار با آزادکن رها کنید.
io-Lines.salam
واردسازی ورودی خروجی

تابع ریشه:
    ناپایا خط‌ها := وکتور {} برگردان وکتور<رشته>
    دیرکن خط‌ها.آزادکن()
    خط‌ها.بیفزا("سیب")
    خط‌ها.بیفزا("انار")
    خط‌ها.بیفزا("انگور")
    بایت := ورودی خروجی.نوشتن خط ها("میوه‌ها.txt"، خط‌ها)
    سرچاپ "نوشته شد:", بایت, "بایت"
    خوانده := ورودی خروجی.خط ها("میوه‌ها.txt")
    دیرکن خوانده.آزادکن()
    هر ش، م در خوانده:
        سرچاپ ش + ۱, م
    پایان
پایان
خروجینوشته شد: 27 بایت 1 سیب 2 انار 3 انگور

نوشتن خطا EPrint

تابع نوشتن خطا(s: رشته)
رشته را در خروجی خطا (stderr) می‌نویسد، بدون رفتن به خط بعد. پیام‌های خطا و هشدار جایشان اینجاست، نه در خروجی عادی برنامه.
io-EPrint.salam
واردسازی ورودی خروجی

تابع ریشه:
    سرچاپ "این در خروجی عادی است"
    ورودی خروجی.نوشتن خطا("هشدار: ")
    ورودی خروجی.نوشتن خطا با خط جدید("این پیام در خروجی خطا می‌رود")
    چاپ "بدون خط تازه..."
    ورودی خروجی.تخلیه()
    سرچاپ ""
پایان
خروجیاین در خروجی عادی است هشدار: این پیام در خروجی خطا می‌رود بدون خط تازه...

نوشتن خطا با خط جدید EPrintln

تابع نوشتن خطا با خط جدید(s: رشته)
مثل نوشتن خطا، ولی پس از پیام به خط بعد می‌رود.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-EPrint.salam

تخلیه Flush

تابع تخلیه()
هر چه در حافظه‌ی میانی خروجی مانده، همین حالا نوشته می‌شود. وقتی با چاپ متنی بدون پایان خط نوشته‌اید و می‌خواهید کاربر فوراً آن را ببیند، به کار می‌آید.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-EPrint.salam

خواندن همه ReadAll

تابع خواندن همه(f: پرونده*): رشته
همه‌ی آنچه از جای فعلی تا پایان پرونده مانده را می‌خواند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-Read.salam

کپی Copy

تابع کپی(dst: پرونده*، src: پرونده*): صحیح۶۴
هر چه در پرونده‌ی src مانده را در پرونده‌ی dst می‌نویسد و تعداد بایت‌های رونوشت‌شده را برمی‌گرداند. برای پرونده‌های بزرگ مناسب است، چون همه‌ی پرونده را یک‌جا در حافظه نمی‌آورد.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-Write.salam

نوشتن خط ها WriteLines

تابع نوشتن خط ها(path: رشته، lines: وکتور<رشته>): صحیح
هر عضو وکتور را در یک خط پرونده می‌نویسد. پرونده اگر باشد از نو نوشته می‌شود. تعداد بایت‌ها یا منفی یک برمی‌گرداند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: io-Lines.salam