10. Файлове в Pascal. Файлове от определен тип. Достъп до данните във файла. Процедури и функции в Borland Pascal за работа с файлове от определен тип.
 

Когато една програма работи с големи обеми от данни, възниква необходимостта те да се съхранят на някакво запаметяващо устройство - твърд диск, дискета, лента, за да може тези данни да се ползват при следващо пускане на програмата или да се ползват от някаква друга програма. 

Дефиниция 10.1

Съхранените на някакво запаметяващо устройство данни се наричат файл

Според вида на данните и начина на тяхната организация има различни видове файлове. В това ръководство се разглежда, как в Borland Pascal се работи с текстови файлови и файлове с опреден тип данни в тях. Според начина на достъп до отделните данни във файловете те биват: с последователен и с пряк достъп. Начинът на достъп зависи и от вида на запаметяващото устройство. Например, лентовите устройства допускат само последователен достъп. Работата с файлове зависи и от операционната система. Различните операционни системи имат различни правила за задаване на запаметяващи устройства, директории и имена на файлове. Например в MS DOS името на файла е една дума до 8 латински букви и цифри дълга, а в MS Windows 95/ 98/ NT/ Me/ 2000/ XP името на файла може да е от няколко думи с обща дължина да 256 символи. В това ръководство ще се предържаме към правилата на MS DOS.

Файл от определен тип.  

Дефиниция 10.2

Файл от определен тип е крайна наредена съвкупност от еднотипни данни на външен носител. 

Отделните елементи могат да бъдат от всякакъв тип с изключение от тип файл. Например елементите на един такъв файл могат да са от тип integer, real, boolean, char, низ от символи, масив, запис.

За да се работи в една програма с файл трябва да се декларира файлова променлива. Деклариране на променлива за файл от определен тип става по следния начин:

    име: file of тип;

където име е името на файловата променлива, а тип е типа на елементите на файла.

Пример 10.1:

    type
        masiv = array[1 .. 100] of real;
        student = record
           
name: string[50];
            fn: longint;
            estim: real

        end
;

    var

        f: file of integer;                 { файл от цели числа }
        names: file of string[50];    { файл от низове от по 50 символа }
        masf: file of masiv;              { файл от масиви от по 100 реални числа }
        studf: file of student;           { файл от записи }

За да се работи с един файл трябва първо да се осъществи връзка между името на файла на външния носител и файловата променлива. Това става чрез библиотечна процедура

    procedure assign(f: file of тип; s: string);

където f е файловата променлива, а низът s задава името на файла на външния носител. Ако е зададено само името на файла, то се счита, че файлът е в същата директория, в която е изпълнимата програма (с разширение .exe), която се получава след компилиране на програмата на Pascal. В низа s може да се зададе име на устройство и път до директорията, в която се намира файла. При това се ползват правилата на съответната операционна система за задаване на устройство и път до директория. След като се направи връзката между файловата променлива и името, файлът трябва де се отвори. Файловете от определен тип се отварят по два различни начина: когато се създава нов файл и когато се чете и пише във вече съществуващ файл. Отварянето на нов файл става чрез библиотечната процедура

    procedure rewrite(f: file of тип);

където f e файловата променлива. Тази процедура създава нов, празен файл с име и място указани преди това в процедурата assign. Ако вече има файл с това име, той се унищожава и се създава новия файл. В новия файл може само да се пише. Отварянето на вече съществуващ файл става чрез библиотечната процедура

    procedure reset(f: file of тип);

където f e файловата променлива. Тази процедура отваря файл с име и място указани преди това в процедурата assign. В така отворения файл може и да се чете, и да се пише.

Във файлове от определен тип може да се четат и пишат само данни от типа на файла. Те са подредени една след друга и данната, която се чете или се пише, се сочи от четящо-пишеща глава (фиг.10.1). При отварянето на файл с reset тази глава сочи първия елемент на файла, който е с номер 0. При всяко четене и писане във файла главата се премества толкова елемента надясно, колкото данни са прочетени или записани.


Фиг.10.1:Файл data.int от 7 цели числа.

Писането във файла става с библиотечната процедура

    write(файлова_променлива, израз1, израз2, ..., изразN)

където стойностите на израз1, израз2, ..., изразN са от типа на елементите на файла. След записването на стойността на съответния израз във файла четящо-пишещата глава се премества един елемент надясно, т.е. след изпълнението на процедурата write главата се е преместила N елемента надясно. Писането може да продължи, докато има място на външния носител.

Четенето от файл става с библиотечната процедура

    read(файлова_променлива, променлива1, променлива2, ...,променливаN)

където променлива1, променлива2, ..., променливаN са от типа на елементите на файла. При прочитане на елемент от файла, сочен от четящо-пишещата глава той се зарежда в съответната променлива и главата се премества към следващия елемент надясно. Така след завършване на процедурата read главата се преместила N елемента надясно. Четенето ще продължи, докато се стигне до края на файла.

Когато завърши работата с файла, той трябва да се затвори. Затварянето на файл става с библиотечната процедура

    procedure close(f: file of тип);

където f е файловата променлива.

Пример 10.2 (Виж пример 10.1):

    var
        i, k, n: integer;
        s: string;

    begin

        { Прочитане и показване на екрана числата от файл data.int. Виж фиг.10.1. }
        assign(f, 'data.int');     { data.int се намира в директорията на програмата }
        reset(f);                     { Отваряне на съществуващ файл }

        for
i :=1 to 7 do begin

            read(f, k);             { Четене на число от файла }
            writeln(k)

        end
;

        close(f);                     { Затваряне на файла }
        { Записване във файл N имена на студенти }

        repeat

            write('Брой имена:');
            readln(n)

        until
n > 0;

        assign(names,'c:\student\students.str'); { Файлът се казва students.str и ще се създаде на устройство c: в директория student }
        rewrite(names);         { Отваряне на нов файл }

        for
i :=1 to n do begin

            write('Студент ', i, ':');
            readln(s);
            write(names, s)     { Записване на низ във файл }

        end
;

        close(names)

    end
.

Когато се чете от един файл, при достигане края на файла трябва да се спре четенето. Проверката за край на файла става чрез библиотечната функция

    function eof(f: file of тип):boolean;

където f е файловата променлива. Тази функция връща true, когато четящо-пишещата глава е достигнала маркера за край на файла (виж фиг.10.1) и false, когато главата сочи елемент от файла. Обикновено тази функция се поставя в условието на оператора за цикъл while.

Пример 10.3:

    var
        a: array[1 .. 100] of real;
        f: file of real;
        i: integer;

    begin

        assign(f, 'data.rel');
        reset(f);
        i := 0;

        while not
eof(f) do begin

            i := i + 1;
            read(f, a[i])

        end
;

        close(f);

    end
.

В този пример се четат реални числа от файл и се поставят в масив. Цикълът while ще се върти, докато главата не е стигнала края на файла.

Отваряйки файла с reset можем да четем и да пишем в него. И при четене, и при писане във файла четящо-пишещата глава се премества надясно. Ако трябва да се позиционира главата върху определен елемент от файла за да се прочете или да се запише нова стойност в него се използва библиотечната процедура

    procedure seek(f: file of тип; k: integer);

където f е файловата променлива, а k е номера на елемент от файла, върху който се позиционира главата. Номерата на елементите във файла се номерират от 0 на нататък, т.е. първият елемент е с номер 0 (виж фиг.10.1). Точно чрез seek се осъществява пряк достъп до файл.

Чрез библиотечната функция

    function filesize(f: file of тип): integer;

се проверява броя на елементите във файл. За файла от фиг.10.1 filesize ще даде 7.

Библиотечната функция

    function filepos(f: file of тип): integer;

определя номера на елемента сочен от четящо-пишещата глава.

Забележка. Библиотечните подпрограми seek, filesize и filepos могат да се ползват само, ако файлът е отворен с reset.

Задача 10.1: Да се състави програма, която

Анализ: Ясно е, че програмата се състои от две части. В първата част се създава нов файл и се записват в него N реални числа, а във втора част се отваря съществуващ файл и по указан номер на елемент и нова стойност на този елемент се позиционира главата и се записва новата стойност.

Програма

program rwfiles;
    uses WinCrt;

    var

        f: file of real;
        i, n: integer;
        a: real;
        name: string[30];
        ch: char;

    begin
        repeat

            writeln('1. Създаване на файл с реални числа');
            writeln('2. Промяна или добавяне на елемент към файл');
            write('Изберете:');
            readln(n)

        until
(n > 0) and (n < 3);
        if
n = 1
            then begin
                         { І част }

                repeat

                    write('Име на файл:');
                    readln(name)

                until
length(name) > 0;

                repeat

                    write('Брой числа:');
                    readln(n)

                until
n > 0;

                assign(f, name);
                rewrite(f);                     { Отваряне на нов файл }
                writeln('Въведете');

                for
i :=1 to n do begin

                    write(i, '-то число:');
                    readln(a);
                    write(f, a)                 { Записване поредното число във файла }

                end
;

                close(f)                         { Затваряне на файла }

            end

            else begin
                       { ІІ част }

                repeat

                    write('Име на файл:');
                    readln(name)

                until
length(name) > 0;

                assign(f, n);
                reset(f);                       { Отваряне на съществуващ файл }

                repeat

                    while not
eof(f) do begin { Извеждане на елементите на файла на екрана }

                        read(f, a);
                        write(a:8:2)

                    end
;

                    writeln;

                    repeat

                        write('Номер на елемент от файла:');
                        readln(n)

                    until
(n >= 0) and (n <= filepos(f)); { Ако n = filepos(f)}, се добавя ново число към края на  файла }

                    seek(f, n);     { Позициониране на главата върху избрания елемент }
                    write('Нова стойност:');
                    readln(a);
                    write(f, a);     { Записване на новото число във файла }
                    seek(f, 0);     { Позициониране на главата върху първия елемент }
                    writeln('Още промени(Y/N):');
                    readln(ch)

                until
(ch = 'N') or (ch = 'n');

                close(f)             { Затваряне на файла }

            end

    end
.

Грешки при работа с файлове. При работа с файлове са възможни най-различни грешки. Например:

В общия случай всяка от тези грешки ще прекъсне програмата и ще се появи съобщение за грешка по време на изпълнение (Runtime - error), което е твърде неприятно, защото може да доведе до загуба на информация или работно време.

Това може да се избегне чрез инструкцията (директивата) {$I-}към компилатора. Чрез тази инструкция се казва на компилатора така да превежда вашата програма, че от мястото на инструкцията нататък в програмата, ако се срещне входно-изходна грешка програмата да не се прекъсва. Това означава, че програмата ще продължи да работи дори при възникване на входно-изходна грешка. Тя обаче ще работи грешно, ако тази грешка не се обработи програмно. Дали е възникнала входно-изходна грешка се установява чрез библиотечната функция

    function ioresult: integer;

Тази функция връща 0, ако предходната входно-изходна операция е протекла успешно. В противен случай тя връща някакво число, съответстващо на възникналата входно-изходна грешка. Значението на тези числа е описано в документацията на средата за програмиране, която се получава при нейното закупуване.

Чрез обратната инструкция {$I+} програмата се връща в режим на прекъсване при възникване на входно-изходна грешка.

Задача 10.2: Да се състави информационна система за служещи във фирма. За всеки служещ да има следната информация: име, длъжност, пол, дата на постъпване, трудов стаж, заплата. Архивът на системата да е записан във файл. Системата да извършва следните дейности:

Програма

program employее;
    uses WinCrt;

    type

        sluzesht = record
            ime: string[50];
            dlaznost: string[20];
            data: string[10];
            staz: integer;
            zaplata: real

        end;
    var

        f: file of sluzesht;
        s: sluzesht;
        k: integer;
        proc: real;
       
find: boolean;
        dlaznost:string[20];

    begin

        assign(f, 'employee.dat');

        repeat

            writeln('1. Пълна справка за всички служещи ');
            writeln('2. Добавяне нов служащ ');
            writeln('3. Имената на служещи от дадена специалност');
            writeln('4. Променя заплатите на всички служещи с определен процент ');
            writeln('0. Изход');
            write('Изберете:');
            readln(k);

            case
k of

                0: ;                     { Изход }

                1: begin             { Пълен списък }
                    {$I-}
reset(f); {$I+}     { Отваряне на съществуващ файл }

                    if
ioresult <> 0              { Грешка при отварянето }
                        then
writeln('Няма служители !')
                        else begin
               { Файлът се е отворил }

                            find := false;

                            while not
eof(f) do begin

                                read(f, s);
                                writeln('Име: ', s.ime);
                                writeln('Длъжност: ', s.dlaznost);
                                writeln('Дата на постъпване: ',s.data);
                                writeln('Стаж: ',s.staz);
                                writeln('Заплата: ', s.zaplata:8:2, 'лв.');
                                writeln;
                                find := true

                            end
;
                            if not
find
                                then
writeln('Няма служители !');

                            close(f)

                        end

                end
;

                2: begin         { Добавяне към файла на нов служител }
                    {$I-} reset(f); {$I+}             { Отваряне на съществуващ файл }

                    if
ioresult <>                      { Грешка при отварянето }
                        then
rewrite(f)                  { Създаване на нов файл }
                        else
seek(f, filesize(f);       { Позициониране главата в края на файла }

                    write('Име:'); readln(s.ime);  { Запълване полетата на записа s }
                    write('Длъжност:'); readln(s.dlaznost);
                    write('Дата на постъпване:'); readln(s.data);
                    write('Стаж:'); readln(s.staz);
                    write('Заплата:'); readln(s.zaplata);
                    write(f, s);                             { Записване на записа s във файла }
                    close(f)

                end
;

                3: begin         { Служители с определена длъжност }
                    {$I-}
reset(f); {$I+}     { Отваряне на съществуващ файл }

                    if
ioresult <> 0              { Грешка при отварянето }
                        then
writeln('Няма служители !')
                        else begin
                { Файлът се е отворил }

                            find := false;
                           
write('Въведете длъжност:');
                            readln(dlaznost);

                            while not
eof(f) do begin

                                read(f, s);
                                if dlaznost = s.dlaznost then begin
                                    writeln('Име: ', s.ime);
                                    writeln('Дата на постъпване: ', s.data);
                                    writeln('Стаж: ', s.staz);
                                    writeln('Заплата: ', s.zaplata:8:2, 'лв.');
                                    writeln;
                                    find := true

                                end

                            end
;
                            if not
find
                                then
writeln('Няма служители с тази длъжност !');

                            close(f)

                        end
                end;

                4: begin         { Промяна на заплатата }
                    {$I-}
reset(f); {$I+}             { Отваряне на съществуващ файл }

                    if
ioresult <>                      { Грешка при отварянето }
                        then
writeln('Няма служители !')
                        else begin
                       { Файлът се е отворил }

                            find := false;
                           
write('Въведете процент на промяна на заплатата:');
                            readln(proc);

                            while not
eof(f) do begin

                                read(f, s);                 { Прочитане на запис }
                                s.zaplata := s.zaplata * (1 + proc / 100);
                                seek(f, filepos(f) -1); { Връщане главата 1 позиция назад }
                                write(f, s);                 { Записване на коригирания запис }
                                find := true

                            end
;
                            if not
find
                                then
writeln('Няма служители !');

                            close(f)

                        end

                end
;
                else
writeln('Грешен избор!')

            end

        until
k = 0;
    end
.


Съдържание


9. Записи. Деклариране и използване на записи. Запис - поле на друг запис. Вариантни записи. Оператор with.


11. Текстови файлове. Процедури и функции в Borland Pascal за работа с текстови файлове. Изтриване и преименуване на файлове от програма на Borland Pascal.