اینجا هستید : safarionline.ir / books / pumr / ch09

فصل ۹ − File Types

🔔 در این فصل به تبعیت از کتاب pascal user manual and report از نماد استفاده کرده‌ایم، حال آن که در کدهای نوشته شده حتی برای کامپایلر Pascal P5 باید از نماد ^ استفاده کنیم.

➡ فهرست

  1. فصل ۹ − File Types
  2. file structure
  3. textfiles

sequnece از بسیاری جهات ساده‌ترین روش ساختاربندی است. در حرفه‌ی data-processing عبارتی که به صورت عمومی برای توصیف یک sequence پذیرفته شده است، sequential file است. پاسکال به سادگی فقط از کلمه‌ی فایل برای مشخص کردن یک ساختار که از توالی‌ای از مولفه‌ها ساخته شده‌است استفاده می‌کند ــ همه‌ی آن مولفه‌ها یا component ها از یک نوع هستند. textfile یک نوع بخصوص از فایل است که از یک توالی از خط‌های با طول متغیر از کاراکترها تشکیل شده است. textfile ها پایه‌های ارتباط قابل خواندن بین انسان‌ها و سیستم‌های کامپیوتری را شکل می‌دهند. شکل زیر به روشن‌تر شدن بحث کمک می‌کند:

component --> sequence --> file --> textfile

💡 در زبان پاسکال file یک structured type است که از یک sequence از component های هم نوع تشکیل شده است.

file structure

ترتیب طبیعی component ها از ابتدا به انتهای sequence تعریف شده است و در هر لحظه فقط یک component به صورت مستقیم قابل دسترسی است. بقیه‌ی component ها به ترتیب با پیش رفتن در فایل قابل دسترسی هستند. تعداد component ها، که طول فایل نامیده می‌شود، در هنگام تعریف فایل به صورت ثابت مشخص نمی‌شود. این ویژگی‌ای است که آشکارا تمایز فایل و آرایه را نشان می‌دهد. به فایلی که حاوی هیچ component ای نباشد فایل خالی یا empty file می‌گویند. بنابراین یک نوع file با انواع array و record و set فرق دارد چون فایل ساختاری با دسترسی ترتیبی یا sequential است که همه‌ی component هایش از یک نوع است.

File  = sequential access + same component type
Array = random access     + same component type + fixed number of components

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

اعلان هر متغیر فایل، مثلا F، به صورت خودکار یک buffer variable را وارد برنامه می‌کند که با نشانه‌ی F↑ بیان می‌شود. این buffer variable از نوع component type است. به buffer variable به عنوان یک دسترسی به فایل نگاه کنید که از طریق آن می‌توان مقدار component های موجود را بررسی کرد (یا خواند) و یا component های جدیدی ایجاد کرد (یا نوشت) و در بعضی file operation ها به صورت خودکار به جلو برده می‌شود.

به کل متغیر فایل یا file variable نمی‌توان assignment کرد. به جای آن می‌توان از buffer variable برای append کردن component ها به صورت تک به تک و ترتیبی (sequential) استفاده کرد. اگر موقعیت فایل بعد از آخرین component اش قرار گیرد buffer variable به صورت undefined در می‌آید.

سینتکس دیاگرام buffer variable به صورت زیر است:

پردازش ترتیبی، طول متغیر، و وجود یک buffer variable نشان می‌دهند که فایل‌ها می‌توانند با حافظه‌ی ثانویه و تجهیرات جانبی مرتبط باشند. اینکه component های فایل چگونه تخصیص داده شوند به پیاده‌سازی زبان/کامپایلر بستگی دارد، اما می‌توانیم فرض کنیم که در هر لحظه از زمان فقط تعدادی از component ها در حافظه‌ی اصلی قرار دارند و فقط component ای که با F↑ مشخص شده است به صورت مستقیم قابل دسترسی است.

🔔 حافظه‌ی ثانویه مثل هارد دیسک و فلاپی و تجهیزات جانبی یا peripherals مثل صفحه کلید و چاپگر.

وقتی که متغیر بافر F↑ به بعد از پایان فایل F برده شده باشد، تابع boolean از پیش تعریف شده‌ی Eof(F) مقدار True و در غیر این صورت False برمی‌گرداند.

پروسیجرهای اصلی برای کار با فایل‌ها عبارتند از:

  1. Reset(F) : با قرار دادن موقعیت مکان‌نمای داخلی فایل در ابتدای آن، بررسی (خواندن) F را آغاز می‌کند. اگر F خالی یا empty نباشد مقدار اولین component آن به F↑ assign می‌شود و Eof(F) نیز False می‌گردد.
  2. Rewrite(F) : تولید (نوشتن) فایل F را آغاز می‌کند. مقدار فعلی F با empty file جایگزین می‌شود (یعنی یک فایل بدون هیچ component ای). Eof(F) مقدار True می‌گیرد و ممکن است یک فایل جدید نوشته شود.
  3. Get(F) : فایل را یک component به جلو می‌برد و مقدار این component را به متغیر بافر F↑ نسبت می‌دهد. اگر compnent بعدی‌ای وجود نداشته باشد Eof(F) مقدار True می‌گیرد و F↑ مقداری تعریف نشده خواهد داشت. اگر Eof(F) مقدار True داشته باشد یا F در حال ایجاد شدن باشد در این صورت فراخوانی پروسیجر Get(F) خطا خواهد داد. #Think
  4. Put(F) : مقدار متغیر بافر F↑ را به فایل F اضافه یا append می‌کند. نتیجه‌ی این عمل خطا است مگر اینکه قبل از اجرای این پروسیجر predicate یا گزاره‌ی Eof(F) مقدار True داشته باشد. بعد از یک Put(F) موفق Eof(F) همچنان True باقی می‌ماند و F↑ تعریف نشده می‌شود. اگر در حال inspection یا خواندن از F باشیم استفاده از Put(F) خطا است.

📝 به طور کلی همه عملیاتِ تولید و بررسی sequential file می‌توانند تماما از طریق چهار file operator ابتدایی و گزاره‌ی Eof بیان شوند.

در عمل ترکیب دو عملیات جلو بردن موقعیت مکان نمای داخلی فایل با دسترسی به buffer variable کاملا عادی و طبیعی است. از همین رو دو پروسیجر زیر را نیز برای کار با فایل‌های ترتیبی فراهم کرده‌ایم:

  1. Read(F, X) : X یک متغیر است. این پروسیجر معادل کد زیر است:

    BEGIN
        X := F↑;
        Get(F)
    END
    
  2. Write(F, E) : E یک expression است. این پروسیجر نیز معادل کد زیر است:

    BEGIN
        F↑ := E;
        Put(F)
    END
    

❗ در واقع Read و Write پروسیجرهای ویژه‌ای هستند که برای پذیرش تعداد متغیری از پارامترهای واقعی توسعه داده شده‌اند. توجه کنید که خود ما به عنوان برنامه‌نویس نمی‌توانیم پروسیجری با variable number of arguments بنویسیم.

⚪ در کد زیر پروسیجر Read معادل compound statement ای است که در ادامه آمده است:

Read(F, V1, ..., Vn)

{ is equivalent to }

BEGIN
    Read(F, V1);
    ...;
    Read(F, Vn)
END

⚪ پروسیجر ویژه‌ی Write و معادل آن که با چند پروسیجر Write پی در پی نوشته می‌شود را در کادر زیر ملاحظه کنید:

Write(F, E1, ..., En)

{ is equivalent to }

BEGIN
    Write(F, E1);
    ...;
    Write(F, En)
END

در مثال‌های بالا V1...Vn متغیر هستند و E1...En هم expression اند.

⚪ مزیت استفاده از پروسیجرهای Read و Write فقط در این نیست که باعث کوتاه‌تر شدن برنامه می‌شوند، بلکه از نظر مفهومی هم ساده‌ترند چون می‌توانیم وجود متغیر بافر F↑، که گاهی اوقات undifined است، را نادیده بگیریم. با این وجود buffer variable را می‌توان به عنوان ابزاری برای «نگاه به جلو» به کار برد:

Reset(F);

WHILE NOT Eof(F) DO
BEGIN
    { look at F↑ and decide what to do }

    Get(F);
END

در کد بالا قبل از اینکه با پروسیجر Get از component فعلی عبور کنیم بر مبنای مقدار متغیر بافر F↑ که حاوی مقدار component فعلی است تصمیم می‌گیریم که در ادامه باید چه کاری انجام دهیم. این همان مفهوم look ahead یا نگاه به جلو است.

⚪ چند مثال از اعلان متغیرهایی از نوع فایل:

VAR
    Data: FILE OF Integer;
    A: Integer;

    PlotFile: FILE OF RECORD
        C: Color;
        Len: Natural
    END;

    Club: FILE OF Person;
    P: Person;

⚪ بر مبنای declaration های بالا چند مثال از statement هایی که با فایل‌ها کار می‌کنند را در زیر ارائه کرده‌ایم:

A := Data↑;
Get(Data);
Read(Data, A);

PlotFile↑.C := Red;
PlotFile↑.Len := 17;
Put(PlotFile);

Club↑ := P;
Put(Club);

Write(Club, P)

⚪ فایل‌ها می‌توانند نسبت به program و یا نسبت به یک پروسیجر local تعریف شوند و یا اینکه از قبل خارج از program وجود داشته باشند. این مورد آخر را external file می‌نامیم. فایل‌های خارجی یا external file ها از طریق program heading و به عنوان پارامتر به برنامه ارسال می‌شوند. ( فصل ۳ را ببینید)

مثال 9.1. این برنامه نه در P5 و نه در Free Pascal و نه در GNU Pascal کامپایل و اجرا نشد.

مثال 9.2. این برنامه نه در P5 و نه در Free Pascal و نه در GNU Pascal کامپایل و اجرا نشد.

مثال. این برنامه در P5 اجرا نشد.

textfiles

textfile ها فایل‌هایی هستند که از یک توالی از کاراکترها تشکیل شده‌اند که به خط‌هایی با طول متغیر تقسیم بندی می‌شوند. نوع از پیش تعریف شده‌ی Text برای اعلان textfile ها استفاده می‌شود.

می‌توانیم فرض کنیم که نوع Text روی نوع داده‌ی اصلی Char و اضافه کردن (فرضی) یک line terminator یا end-of-line marker ایجاد شده است. بنابر این نوع Text معادل PACKED FILE OF Char نیست. این end-of-line marker می‌توانند توسط پروسیجرها و توابع مخصوص textfile زیر تولید شده و یا تشخیص داده شوند:

  1. Writeln(F) : خط جاریِ textfile داده شده را پایان می‌دهد یا terminate می‌کند.
  2. Readln(F) : به ابتدای خط بعدی textfile داده شده پرش یا skip می‌کند. F↑ برابر اولین کاراکتر خط بعد می‌شود.
  3. Eoln(F) : یک تابع Boolean است که بیان می‌کند آیا به end-of-line خط جاری در textfile داده شده رسیده‌ایم یا نه. اگر بله، F↑ معادل مکان line seperator خواهد بود ولی خود F↑ دارای مقدار blank است.

❓ با نگاه کردن به کد مثال‌های Read1 و ReadLn1 سعی کنید رفتار پروسیجرهای Read و ReadLn و Write و WriteLn و توابع Eoln و Eof را تحلیل کنید.

⚪ اگر F یک textfile و Ch یک متغیر از نوع کاراکتر باشد Write(F, Ch) معادل و نسخه‌ی مختصر تکه کد زیر است:

BEGIN
    F^ := Ch;
    Put(F)
END

⚪ پیاده سازی Read(F, Ch) به دو صورت ممکن است و انتخاب یکی از این دو شیوه وابسته به برنامه‌نویسان کامپایلر است:

  1. کاراکتر واقع در موقعیت فعلی فایل F را در متغیر Ch می‌ریزد.
  2. مقدار F↑ را در متغیر Ch می‌ریزد.

در هر دو حالت بعد از assignment، پروسیجرِ Get(F) اجرا می‌شود.

⚪ Input و Output نام دو متغیر textfile استاندارد هستند که به عنوان پارامترهای program برای خواندن و نوشتن text به صورت واضح و خوانا استفاده می‌شوند. در فصل ۱۲ به تفصیل در مورد این متغیرها و فرم‌های توسعه یافته‌ی پروسیجرهای Read و Write و ReadLn و WriteLn صحبت خواهیم کرد.

تکه کدهای زیر از قراردادهای پیش‌گفته برای نشان دادن بعضی از عملیات‌های رایج بر روی textfile ها استفاده می‌کنند:

  1. نوشتن در یک textfile.

    فرض کنید که P(C) یک کاراکتر (بعدی) را محاسبه می‌کند و آن را در پارامتر C قرار می‌دهد. برای terminate شدن خط فعلی، متغیر B1 به True ست می‌شود و برای نشان دادن اینکه متن باید خاتمه یابد متغیر B2 به True مقداردهی می‌شود:

    Rewrite(Y);
    REPEAT
        REPEAT
            P(C);
            Write(Y, C)
        UNTIL B1;
        WriteLn(Y)
    UNTIL B2
    
  2. خواندن از یک textfile.

    فرض کنید که Q(C) بیانگر پردازش یک کاراکتر (بعدی) C باشد و R نیز عملی است که در صورت مواجه شدن با end-of-line باید اجرا شود:

    Reset(X);
    WHILE NOT Eof(X) DO BEGIN
        WHILE NOT Eoln(X) DO BEGIN
            Read(X, C);
            Q(C)
        END;
    
        R;
        ReadLn(X)
    END
    
  3. کپی کردن یک textfile در textfile دیگر با حفظ line structure فایل اول:

    Reset(X);
    Rewrite(Y);
    
    WHILE NOT Eof(X) DO BEGIN
        WHILE NOT Eoln(X) DO BEGIN      { copy a line }
            Read(X, C);
            Write(Y, C)
        END;
    
        ReadLn(X);
        WriteLn(Y)
    END
    

    مثال. با GNU Pascal کامپایل شود.

 

🔵 نکته‌ای در باب پیاده‌سازی: یک روش سرراست برای بیان نشانگر end-of-line استفاده از کاراکترهای کنترلی است. به عنوان مثال، در مجموعه‌ی کاراکترهای اسکی، دو کاراکتر cr یا carriage return و lf یا line feed به صورت قراردادی برای نشان دادن پایان یک خط استفاده می‌شوند. به هر حال، بعضی سیستم‌های کامپیوتری از مجموعه کاراکترهایی عاری از چنین control character هایی استفاده می‌کنند؛ این موضوع باعث می‌شود که از روش‌های دیگری برای بیان end-of-line استفاده کرد.

پیاده سازی دستور wc سیستم‌های یونیکس

© کلیه‌ی حقوق برای safarionline.ir محفوظ است.