9. Записи. Деклариране и използване на записи. Запис - поле на друг запис. Вариантни записи. Оператор with.
 
Дефиниция 9.1 Запис е крайна наредена структура от данни, състояща се от разнотипни елементи, наречени полета.

Записът служи да групира в себе си разнотипната информация, описваща някакъв сложен информационен обект. Например за студент може да имаме следните данни: име - низ от символи, факултетен номер - цяло число, среден успех - реално число и т.н.

Когато в програмата трябва да се използват записи, обикновено се декларира тип - запис и после се декларират променливи, параметри на подпрограми, файлове от такъв тип - запис. Декларирането на тип - запис става в секцията type на програмата по следния начин:

    име = record
        поле1: тип1;
        поле2: тип2;
        ..........
        полеN: типN
    end;

където име е името на новия тип, поле1, поле2, ..., полеN са имената на полетата, от които се състои записа, а тип1, тип2, ..., типN са съответно техните типове. Може направо да се декларират променливи - записи без предварително да се декларира съответен тип запис.

Пример 9.1:

    type
        student = record
            name: string[50];         { поле 1 - име на студент }
            fn: longint;                   { поле 2 - факултетен номер - дълго цяло число }
            estim: real;                   { поле 3 - оценка }
        end;
    var
        s: student;                         { променлива запис }
        polygon: record                { променлива запис }
            strana: real;
            broj: integer
        end;
         group: array[1 .. 20] of student;     { масив от записи }

    procedure vhod(var s:student);         { параметър запис }
         ..........

Променливите и параметрите записи се ползват в програмата чрез обръщение към техните полета по следния начин:

    име.поле

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

Пример 9.2 (виж пример 9.1):

    begin
        write('Име на студент:');           { въвеждане на полетата на запис }
        readln(s.name);
        write('Факултетен номер:');
        readln(s.fn);
        write('Среден успех:');
        readln(s.estim);
         if s.name = 'Иван Петров Иванов'
             then group[1] := s;                  { присвояване на един запис - друг запис }
        .............
         for i: = 1 to 20 do begin
            writeln('Име ', group[i].name);  { обръщение към поле от запис - елемент на масив }
             writeln('Факултетен номер ', group[i].fn);
            writeln('Среден успех ', group[i].estim:5:1);
        end;
        ...............
        write('Брой страни на многоъгълник:');
        readln(polygon.broj);
        write('Дължина на страна:');
        readln(polygon.strana);
        .........
    end.

Полетата на един запис могат да са не само скаларни данни, но и структури масиви, низове, записи. В следващия пример имаме вложени записи.

Пример 9.3:

    type
        factura = record
            nomer: integer;
            cena: real;
            ime: string[60];
        end;
        detail = record
            ime: string[30];
            broj: integer;
            dostavka: factura                         { поле - запис }
        end;
    var
        sklad: array[1 .. 100] of detail;
    begin
         for i := 1 to 100 do begin
            write('Име на детайл:');
            readln(sklad[i].ime);
            write('Брой:');
            readln(sklad[i].broj);
            write('Номер на фактура:');
            readln(sklad[i].dostavka.nomer); { обръщение към подполе на поле - запис }
            write('Цена:');
            readln(sklad[i].dostavka.cena);
            write('Име на доставчик:');
            readln(sklad[i].dostavka.ime)
        end;
         .................

В горния пример записът detail има поле dostavka, което е също запис, т.е. имаме вложени записи. До полетата на вложения запис достигаме като приложим два пъти знака точка. Обърнете внимание, че sklad[i].ime и sklad[i].dostavka.ime са две различни полета.

Задача 9.1: Да се състави програма, която изпълнява следните дейности:

Анализ
Входни данни: k - цяло - вид дейност на програмата:
         1 - добавяне на студент към масива, 
         2 - информация за всички студенти,
         3 - промяна на успеха,
         0 - изход.
    При въвеждане информация за студент: 
        name - низ от символи - име на студент, 
        fn - дълго цяло - факултетен номер, 
        estim - реално - факултетен номер.

    При промяна успеха на студент: 
        fn - факултетен номер, 
        estim - успех.

Изходни данни: Меню с дейностите на програмата, полетата на елементите-записи в масива, различни съобщения: за грешен избор от менюто, за липса на студенти в масива и т.н.

Алгоритъм: Програмата ще работи циклично, като ще завърши при избор за изход от менюто (k = 0). При всяко завъртане в този цикъл ще изпълнява една от своите дейности според избора от менюто.

Програма

program student_info;
    uses
WinCrt;

    const
max = 100;
    type

        student = record
           
name: string[50];
            fn: longint;
            estim: real

        end;

        group = array[1 .. max] of student;

    var
        k, n: integer
;         { k - избор от менюто, n -брой студенти в масива }
        g: group;                 { масив от студенти }

    procedure dobavi(var g: group; var n: integer);    { Добавяне на студент към масива }
        begin
            if
n = max
                then
writeln('Масивът със студенти е пълен !')
                else begin

                    n:= n + 1;
                    write('Име на студент:');
                    readln(g[n].name);
                    write('Факултетен номер:');
                    readln(g[n].fn);
                    write('Среден успех:');
                    readln(g[n].estim);

                end
        end;

    procedure info(g: group; n: integer);    { Извеждане на информация за всички студенти }
        var
i: integer;
        begin
            if
n = 0
                then
writeln('Масивът със студенти е празен !')
                else
                    for
i := 1 to n do begin
                        writeln('Име на студент:', g[i].name);
                        writeln('Факултетен номер:', g[i].fn);
                        writeln('Среден успех:', g[i].estim:5:2);
                        readln

                    end
        end
;

    function change(var g: group; n: integer): boolean;    { Промяна на ср. успех на студент }
        var

            i: integer;
            fn: longint;         { факултетен номер на търсения студент }
            find: boolean;     { показва дали е открит студент с указания факултетен номер }

        begin

            find:= false;
           
write('Факултетен номер на търсения студент:');
            readln(fn);

            for
i := 1 to n do
                if
fn = g[i].fn
                    then begin

                        find:= true;
                        writeln('Име на студент:', g[i].name);
                        writeln('Стар среден успех:', g[i].estim:5:2);
                        write('Нов среден успех:');
                        readln(g[i].estim)

                    end
;
            change := find

        end
;

    begin             { Главна програма }
        n:= 0;         { няма студенти в масива }

        repeat

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

            case
k of
               
0: ;         { празен оператор }
                1: dobavi(g, n);
                2: info(g, n);
                3: if not change(g, n)

                        then
writeln('Няма такъв студент !');
                else
writeln('Грешен избор !')
            end
        until
k = 0
    end.

Забележка: Програми от този вид се наричат информационни системи. Информационните системи се характеризират с това, че притежават архив (в този случай масива g) и имат средства за допълване (дейност 1) и промяна на единиците на архива (дейност 3), а също така могат да извършват справки в архива (дейност 3).

Понякога в един запис в едни случаи трябва да има едни полета, а в други случай други. Тогава се използват вариантни записи. Например в записа student (виж пример 9.1) полето estim служи за среден успех на студента. Възможно е обаче студента да е прекъснал и да няма среден успех. В този случай би трябвало да има друго поле от низ от символи, в което да се посочи причини за прекъсване.

Всички вариантни полета се декларират след постоянните полета на записа по следния начин:

    име = record
        постоянни полета

        case
ключ: изброим_тип of
            случай1: (полета1);
            случай2: (полета2);
            ...........
            случайN: (полетаN)

    end;

където постоянните полета се декларират като при обикновените записи, полето ключ определя, кой от вариантите на записа се използва, случай1, случай2, ..., случайN са стойности (константи) на полето ключ, указващи при каква стойност на ключ какви вариантни полета се избират и полета1, полета2, ..., полетаN са полетата за съответния вариант. Може сред някои от тези полета също да имаме вариант и така се получават сложни вложени варианти.

Пример 9.4:

    type
       
student = record
            ime: string[50];
            fn: longint;

            case
izpit: boolean of                 { ключово поле показващо дали студента е изпитан }
                true:
( lektor: string[50];       { име на преподавателя }
                           estim: real );               { оценка }

                false
: ( note: string[100])      { бележка обясняваща неявяване на изпит }
        end;
    var

        s1, s2: student;

    begin

        s1.ime := 'Иван Петров Иванов';         { I студент }
        s1.fn := 992212;
        s1.izpit := true;
        s1.lektor := 'доц. Петров';
        s1.estim := 3.5;
        s2.ime := 'Ана Иванова Петрова';      { II студент}
        s2.fn := 992213;
        s2.izpit := false;
        s2.note := 'в майчинство';
        ...........

    end
.

Подредбата на полетата в записа има значение само при представянето му в паметта на компютъра. Полетата се разполагат в паметта по реда на тяхното деклариране. Размерът на данна от тип запис е равен на сумата от дължините на полетата, от който тя се състои. При вариантните записи се взема дължината на най-дългия вариант. Ако в програмата трябва да определите дължината на един запис в брой байтове, маже да се използва следната библиотечна функция:

function sizeof(тип): integer;

където параметърът тип е някой от декларираните в секция type типове или някой от вградените в езика типове. Например sizeof(longint) = 4, а за student от пример 9.4 sizeof(student) = 157.

Ако разгледате отново примери 9.2 и 9.3, може да забележите, че при обръщение към полетата на един запис трябва да се пише всеки път и името на записа. В пример 9.3 пък при обръщение към подполетата на полето dostavka трябва де се пише всеки път името на записа и името на полето, към чието подполета става обръщението. В дадена програмна част това може да бъде многократно и да доведе до досадно преписване на едни и същи неща. За този случай в езика Pascal съществува оператор with, чрез който се съкращава това досадно преписване. Този оператор има следния вид:

    with име_на запис do
   
     команда

където име_на запис е името на записа, към чието полета се обръщаме, а в команда, която почти винаги е съставния оператор, се обръщаме направо към тези полета без да пишем името на записа. име_на_запис може да бъде обръщение към поле-запис. Тогава в команда се обръщаме към подполетата без да пишем името на записа и на неговото поле-запис

Пример 9.5 (извеждането на масива group в края на пример 9.2 ползвайки оператор with):

    for i:= 1 to 20 do
        with
group[i] do begin
            writeln('Име: ', name);
            writeln('Факултетен номер: ', .fn);
            writeln('Среден успех: ', estim:5:2)

        end;

Задача 9.2: Да се създаде информационна система, в която има следната информация за автомобили от транспортно предприятие: марка автомобил, номер, брой километри и състояние на автомобила, което е:

  1. на път - краен пункт, разстояние, шофьор;
  2. в ремонт - вид ремонт, цена;
  3. в престой - брой дни.
Системата да може да извършва следните дейности: добавяне на нов автомобил, премахване на автомобил, пълна справка за всички автомобили, справка за автомобили с пробег в даден диапазон.

Алгоритъм: Тази задача е подобна на задача 9.1. Ще използваме запис с варианти за състоянието на автомобила - 1 на път - три полета, 2 в ремонт - две полета и 3 в престой - едно поле. При изтриването на автомобил от масива:

    1. Открива се автомобила с номер еднакъв с този, който искаме да изтрием.
    2. Ако такъв автомобил е открит и той не е последния,
        всички автомобили след него ги преместваме в масива един елемент напред.
    3. Ако автомобил е изтрит,
   
     намаляваме броя на автомобилите с един.

Програма

program transport;
    uses
WinCrt;

    const
max = 50;
    type

        automobil = record
            marka: string[20];
            nomer: string[15];
            probeg: longint;

            case
sastojanie: integer of
               
1:(punkt: string[20];
                    razstojanie: integer;
                    shofior: string[20]);
                2:(vid: string[20];
                    cena: real);
                3:(brojdni: integer);

        end;

        autopark = array[1 .. max] of automobil;

    var

        k, n: integer;
       
auto: autopark;

    procedure dobavi(var a: autopark; var n: integer);
        begin
            if
n = max
                then
writeln('Автопаркът е пълен !')
                else begin

                    n := n + 1;

                    with
a[n] do begin
                       
write('Марка на автомобила:');
                        readln(marka);
                        write('Номер:');
                        readln(nomer);
                        write('Пробег в км:');
                        readln(probeg);

                        repeat

                            write('Състояние на автомобила 1.На път, 2.В ремонт, 3. В престой');
                            readln(sastojanie);

                            case
sastojanie of
                               
1: begin
                                   
write('Краен пункт:');
                                    readln(punkt);
                                    write('Разстояние в км:');
                                    readln(razstojanie);
                                    write('Шофьор:');
                                    readln(shofjor)

                                end
;
                                2: begin
                                   
write('Вид на ремонта:');
                                    readln(vid);
                                    write('Цена в лв:');
                                    readln(cena)

                                end
;
                                3: begin
                                   
write('Брой дни на престоя:');
                                    readln(brojdni)

                                end
;
                                else
writeln('Грешен избор на състояние !')
                            end
                        until
(0 < sastojanie) and (sastojanie < 4);
                    end
                end
        end;

    procedure delauto(var a: autopark; var n: integer);
        var

            i: integer;
           
numb: string[15];
            find: boolean;

        begin
            if
n = 0
                then
writeln('Няма автомобили в автопарка !')
                else begin
                    write('Номер на автомобил за изтриване:');
                    readln(numb);
                    find := false;

                    for
i := 1 to n do
                        if
find and (i < n)
                            then
a[i] := a[i + 1]
                        else if
numb = a[i].nomer         {автомобилът е открит}
                            then begin

                                find := true;

                                if
i < n
                                    then
a[i] := a[i + 1]
                            end;
                    if
find
                        then
n := n - 1
                        else
writeln('Няма автомобил с такъв номер !')
                end
        end;

    procedure info(a:autopark; n: integer);
        var
i: integer;
        begin
            if
n = 0
                then
writeln('Няма автомобили в автопарка !')
            else
                for
i := 1 to n do begin
                    with
a[i] do begin
                        writeln('Марка на автомобила:', marka);
                        writeln('Номер:', nomer);
                        writeln('Пробег в км:', probeg);
                        write('Състояние на автомобила - ');

                        case
sastojanie of
                           
1: begin

                                writeln('на път.');
                                writeln('Краен пункт:', punkt);
                                writeln('Разстояние в км:', razstojanie);
                                writeln('Шофьор:', shofjor)

                            end;

                            2: begin
                                writeln('в ремонт.');
                                writeln('Вид на ремонта:', vid);
                                writeln('Цена в лв:', cena:10:2)

                            end;

                            3: begin
                                writeln('в престой.');
                                writeln('Брой дни на престоя:', brojdni)

                            end
                        end
                    end

                    readln

                end
        end;

    procedure infoprobeg(a:autopark; n: integer);
        var

            i, pmn, pmx: integer;
           
find: boolean;

        begin
            if
n = 0
                 then writeln('Няма автомобили в автопарка !')
            else begin
                repeat

                    write('Въведете минимален и максимален пробег:');
                    readln(pmn, pmx)

                until
pmn < pmx;
                find := false;
                for i := 1 to n do begin

                    with
a[i] do begin
                        if (
pmn <= probeg and (probeg <= pmx)
                            then begin

                                find := true
                                writeln('Марка на автомобила:', marka);
                                writeln('Номер:', nomer);
                                writeln('Пробег в км:', probeg);

                            end
                    end;

                    readln

                end;
                if not
find
                    then
writeln('Няма автомобили с пробег в указания диапазон !')
            end
        end;

    begin                 { Главна програма }
        n:= 0;             { няма автомобили в масива }

        repeat

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

            case
k of
               
0: ;             { празен оператор }
                1: dobavi(auto,n);
                2: info(auto,n);
                3: delauto(auto,n);
                4: infoprobeg(auto,n);

                else
writeln('Грешен избор !')
            end
        until
k = 0
    end.


Съдържание


8. Обработка на символна информация в Pascal. Символен тип и тип низ от символи. ASCII таблица в DOS и ANSI таблица в Windows. Операции с отделни символи и с низове от символи. Вградени подпрограми в Borland Pascal за работа със символи. 


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