Программирование на языке высокого уровня Паскаль

Типы данных, определяемые программистом

Разбить на страницы
Показывать лекцию целиком

Презентацию к данной работе Вы можете скачать здесь.

Информация, которую требуется обрабатывать в программе, имеет различную структуру. Для ее адекватного представления используются типы данных, которые программист определяет сам в разделе описания типов type. Типу дается произвольное имя, которое можно затем использовать для описания программных объектов точно так же, как и стандартные имена типов.

type имя_типа = описание_типа
...
var имя_переменной : имя_типа

Можно задать тип и непосредственно при описании переменных:

var имя_переменной : описание_типа

Перечисляемый тип данных

При написании программ часто возникает потребность определить несколько связанных между собой именованных констант, имеющих различные значения. Для этого удобно воспользоваться перечисляемым типом данных, все возможные значения которого задаются списком констант.

type имя_типа = (список имен констант)

Константы в списке перечисляются через запятую, например:

type Menu = (READ, WRITE, EDIT, QUIT)

Переменным перечисляемого типа можно присвоить либо значение одной из перечисленных констант, либо значение другой переменной того же типа, например:

var m, n : Menu;
…
m := READ; n := m;

Перечисляемый тип относится к порядковым типам данных. Константы в списке нумеруются с нуля. Например, Ord(READ) даст в результате 0, Succ(EDIT) QUIT. Попытка получения значения, следующего за последним, приведет к ошибке.

Использовать перечисляемый тип в операциях ввода-вывода нельзя. Имена констант в пределах области их описания (программы или подпрограммы) должны быть уникальными.

Интервальный тип данных

С помощью интервального типа задается диапазон значений какого-либо типа.

type имя_типа = константа_1 .. константа_2

Константы должны быть одного и того же порядкового типа. Тип, на котором строится интервал, называется базовым. Константа_1 должна быть меньше или равна константе_2. Примеры описания интервальных типов:

type Hour = 0 .. 23;
     Range = –100 .. 100;
     Letters = 'a' .. 'z';
     Actions = READ .. EDIT;

Как и для других типов, определяемых программистом, интервальный тип можно задать прямо при описании переменной, например:

var r : –100 .. 100;

С переменной интервального типа можно делать все, что допустимо для ее базового типа. Ее значение должно находиться в указанном диапазоне, в противном случае произойдет ошибка времени выполнения 'Constant out of range'.

Интервальный тип используется в программах как самостоятельно, так и внутри определения массива.

Массивы

При использовании простых переменных каждой области памяти, предназначенной для хранения какого-либо значения, соответствует свое имя. Если с группой величин одинакового типа требуется выполнять однообразные действия, им дают одно имя, а различают по порядковому номеру (индексу). Это позволяет компактно записывать множество операций с помощью циклов.

Конечная именованная последовательность однотипных величин называется массивом. Чтобы описать массив, надо определить, какого типа его элементы и каким образом они пронумерованы (какого типа его индекс).

type имя_типа = array [тип_индекса] of тип_элемента

Здесь array и of — ключевые слова, тип индекса задается в квадратных скобках. Примеры описания типа:

type  mas    = array [1 .. 10] of real;
      Color  = array [byte] of mas;
      Active = array [Menu] of boolean;

В первом операторе описан тип массива из вещественных элементов, которые нумеруются от 1 до 10. Во втором операторе элементами массива являются массивы типа mas, а нумеруются они в пределах, допустимых для типа byte, то есть от 0 до 255. В третьей строке в качестве индекса использовано имя типа из раздела 'Перечисляемый тип данных', а сами элементы могут принимать значения true или false.

Тип элементов массива может быть любым, кроме файлового, тип индексов — интервальным, перечисляемым или byte. Чаще всего для описания индекса используется интервальный тип данных.

ВНИМАНИЕ Размещение массива в памяти происходит до выполнения программы, поэтому при описании индекса можно применять только константы или константные выражения.

Обычно при описании массива верхняя граница его индекса задается в виде именованной константы, например:

const n = 6;
type intmas = array [1 .. n] of integer;

После задания типа массива переменные этого типа описываются обычным образом:

var a, b : intmas;

С массивами в целом можно выполнять только одну операцию: присваивание. При этом массивы должны быть одного типа:

b := a;

Все остальные действия выполняются с отдельными элементами массива. Для обращения к элементу массива после имени массива указывается номер элемента в квадратных скобках:

a[4] b[i]

С элементом массива можно делать все, что допустимо для переменных того же типа.

При обращении к элементу массива автоматический контроль выхода индекса за границу массива не производится. Для включения режима автоматического контроля необходимо добавить в любое место программы, предшествующее обращениям к элементу, ключ компиляции {$R+} или установить соответствующий режим в оболочке.

Инициализация массивов.Элементам массива можно присвоить значения до начала выполнения программы. Это делается так же, как и для простых переменных, — в разделе описания констант, например:

const a : intmas = (0, 5, –7, 100, 15, 1);

Количество констант должно точно соответствовать числу элементов массива. Массивы, описанные в разделе var главной программы, обнуляются автоматически.

Рассмотрим задачу поиска максимального элемента массива. Очевидно, что для отыскания самого большого элемента нужно сравнить между собой все элементы массива. Элементы выбираются попарно. При каждом сравнении из двух чисел выбирается наибольшее. Поскольку его надо где-то хранить, в программе описывается переменная того же типа, что и элементы массива. После окончания просмотра массива в ней окажется самый большой элемент. Перед началом просмотра в эту переменную заносится какой-либо элемент массива.

Сформулируем алгоритм поиска максимума.

  • Принять за максимальный первый элемент массива.
  • Просмотреть массив, начиная со второго элемента.
  • Если очередной элемент оказывается больше максимального, принять его за максимальный.
  • Программа приведена в .

    program max_elem;
    const n = 20;
    var a   : array [1 .. n] of real;
        i   : integer;
        max : real;
    begin
        writeln('Введите ', n, ' элементов массива');
        for i := 1 to n do read(a[i]);
        max := a[1];                 { принять за максимальный первый элемент массива }
        for i := 2 to n do          { просмотреть массив, начиная со второго элемента }
            if a[i] > max then max := a[i];     { при необходимости обновить максимум }
        writeln('Максимальный элемент: ', max:6:2)
    end.

    Еще один простой пример работы с массивом приведен в .

    program sum_num;
    const n = 10;
    var a : array [1 .. n] of integer;
        i, sum, num : integer;
    begin
        writeln('Введите ', n, ' элементов массива');
        for i := 1 to n do read(a[i]);
        sum := 0;     
        num := 0; 
        for i := 1 to n do begin
            sum := sum + a[i];
            if a[i] < 0 then inc(num);
        end;
        writeln('Сумма элементов: ', sum);
        writeln('Отрицательных элементов: ', num);
    end.

    Двумерные массивы

    Элемент массива может быть любого типа, кроме файлового, следовательно, он может быть и массивом, например:

    const n = 4; m = 3;
    type mas  = array [1 .. n] of integer;    
         mas2 = array [1 .. m] of mas;

    Более компактно это можно записать так:

    type mas2 = array [1 .. m, 1 .. n] of integer;

    Здесь описан тип массива, состоящего из ). Обе размерности массива должны быть константами или константными выражениями. Имя типа указывается при описании переменных, например:

    var a, b : mas2;

    В памяти двумерный массив располагается по строкам.

    a11  a12  a13  a14  a21  a22   a23  a24  a31  a32  a33  a34
    |  – 1–я строка –   |   – 2–я строка –    |   – 3–я строка –   |

    Строки массива ничем не отделены одна от другой, то есть прямоугольной матрицей двумерный массив является только в нашем воображении. При просмотре массива от начала в первую очередь изменяется правый индекс (номер столбца).

    (рис 3.1) Матрица из m строк и n столбцов

    К элементу двумерного массива обращаются, указывая номер строки и номер столбца, на пересечении которых он расположен, например:

    a[1, 4] b[i, j] b[j, i]

    ВНИМАНИЕ Компилятор воспринимает как номер строки первый индекс, как бы он ни был обозначен в программе.

    При инициализации двумерных массивов каждая строка заключается в дополнительную пару круглых скобок, например:

    const a : mas2 = ( ( 2, 3, 1, 0),
                       ( 1, 9, 1, 3),
                       ( 3, 5, 7, 0)  );

    С массивами в целом определена только одна операция — присваивание массивов одного типа (например, b := a ) . Все остальные действия выполняются с отдельными элементами. Например, чтобы ввести с клавиатуры двумерный массив, необходимо организовать вложенные циклы:

    for i := 1 to m do
        for j := 1 to n do read(a[i, j]);

    В соответствии с приведенным здесь порядком следования циклов элементы массива должны вводиться по строкам (при этом неважно, как будут располагаться элементы массива, важен только порядок их следования).

    Пример. Программа, которая для целочисленной матрицы 3x4 определяет среднее арифметическое ее элементов и количество положительных элементов в каждой строке.

    Для нахождения среднего арифметического порядок перебора элементов массива (по строкам или по столбцам) роли не играет. Нахождение количества положительных элементов каждой строки требует просмотра матрицы по строкам ().

    program sred_n;
    const m = 3; n = 4;
    var a : array [1 .. m, 1 .. n] of integer;
        i, j, n_pos_el : integer;
        sred : real;
    begin
        for i := 1 to m do
        for j := 1 to n do read(a[i, j]);
        sred := 0;
        for i := 1 to m do begin
            n_pos_el := 0;
            for j := 1 to n do begin
                sred := sred + a[i, j];
                if a[i, j] > 0 then inc(n_pos_el);
            end;
            writeln('В ', i, '–й строке ', n_pos_el, ' положительных элементов');
        end;
        sred := sred / m / n;
        writeln('Среднее арифметическое: ', sred:6:2);
    end.
    СОВЕТ Записывайте операторы инициализации накапливаемых в цикле величин непосредственно перед циклом, в котором они вычисляются.

    Строки

    Строки используются для хранения последовательностей символов. В Паскале существует три типа строк:

  • стандартные ( string );
  • определяемые программистом на основе string ;
  • строки в динамической памяти
  • Строка типа string может содержать до 255 символов. Под каждый символ отводится по одному байту, в котором хранится код символа. Еще один байт отводится под фактическую длину строки. Таким образом, в памяти под одну переменную типа string всегда отводится 256 байт.

    Для коротких строк использовать стандартную строку неэффективно, поэтому есть возможность самостоятельно задавать максимальную длину строки, которая должна быть константой или константным выражением. Например, ниже описан собственный тип данных с именем str4:

    type str4 = string [4]; { переменная такого типа занимает в памяти 5 байтов }

    Примеры описания строк:

    const n = 15;
    var s : string;         { строка стандартого типа }
        s1 : str4;          { строка типа str4, описанного выше }
        s2 : string [n];    { описание типа задано при описании переменной }

    Инициализация строк, как и переменных других типов, выполняется в разделе описания констант.

    const s3 : string [15] = 'shooshpanchik';

    Внутреннее представление строки .

    (рис 3.2) Внутреннее представление строки s3

    Операции

    Строки можно присваивать друг другу. Если максимальная длина результирующей строки меньше длины исходной, лишние символы справа отбрасываются.

    s2 := 'shooshpanchik'; 
    s1 := s2;                  { в s1 будут помещены символы "shoo" }

    Строки можно склеивать (сцеплять) между собой с помощью операции конкатенации, которая обозначается знаком +, например:

    s1 := 'ком';
    s2 := s1 + 'пот';                        { результат — "компот" }

    Строки можно сравнивать друг с другом с помощью операций отношения. При сравнении строки рассматриваются посимвольно слева направо, при этом сравниваются коды соответствующих пар символов. Строки равны, если они имеют одинаковую длину и посимвольно эквивалентны. В строках разной длины существующий символ всегда больше соответствующего ему отсутствующего символа.

    'abc' > 'ab' 'abc' = 'abc' 'abc' < 'abc '

    Имя строки может использоваться в процедурах ввода-вывода:

    readln (s1, s2); write (s1);

    При вводе в строку считывается из входного потока количество символов, равное длине строки или меньшее, если символ перевода строки (который вводится нажатием клавиши Enter ) встретится раньше. При выводе под строку отводится количество позиций, равное ее фактической длине.

    К отдельному символу строки можно обращаться как к элементу массива символов, например s1[4] . Символ строки совместим с типом char, их можно использовать в выражениях одновременно, например:

    s1[4] := 'x'; writeln (s2[3] + s2[5] + 'r');

    Процедуры и функции для работы со строками

    При работе со строками, как правило, возникает необходимость выполнять их копирование, вставку, удаление или поиск. Для эффективной реализации этих действий в Паскале предусмотрены стандартные процедуры и функции. Они кратко описаны ниже.

    Функция Concat (s1, s2, ..., sn) возвращает строку, являющуюся слиянием строк s1, s2, ..., sn.

    Функция Copy (s, start, len) возвращает подстроку длиной len, начинающуюся с позиции start строки s.

    Процедура Delete (s, start, len) удаляет из строки s, начиная с позиции start, подстроку длиной len.

    Процедура Insert (subs, s, start) вставляет в строку s подстроку subs, начиная с позиции start.

    Функция Length (s) возвращает фактическую длину строки s, результат имеет тип byte.

    Функция Pos (subs, s) ищет вхождение подстроки subs в строку s и возвращает номер первого символа subs в s или нуль, если subs не содержится в s.

    Процедура Str (x, s) преобразует числовое значение x в строку s, при этом для x может быть задан формат, как в процедурах вывода write и writeln.

    Процедура Val (s, x, errcode) преобразует строку s в значение числовой переменной x, при этом строка s должна содержать символьное представление числа. В случае успешного преобразования переменная errcode равна нулю. Если же обнаружена ошибка, то errcode будет содержать номер позиции первого ошибочного символа, а значение x не определено.

    Пример. Программа читает текст из файла и выводит его на экран, заменяя заданную с клавиатуры последовательность символов на многоточии ().

    program censor;
    var s, str : string[10];
        f      : text;
        i, dl  : integer;
    begin
        assign(f, 'primer.txt'); reset(f);
        writeln('Какую последовательность заменять?'); readln(s);
        dl := length(s);
        while not Eof(f) do begin
            readln(f, str);
            i := 1;
            while i <> 0 do begin
                i := Pos(s, str);
                if i <> 0 then begin
                    Delete(str, i, dl); Insert('...', str, i);
                end;
            end;
            writeln(str);
        end;
        close(f)
    end.

    Записи

    В программах часто возникает необходимость логического объединения разнородных данных (например, база данных предприятия содержит для каждого сотрудника его фамилию, дату рождения, должность, оклад и т. д.). Для этого предназначен тип 'запись'. Он вводится с помощью ключевого слова record. Элементы записи называются полями.

    type имя_типа = record
        описание 1-го поля записи;
        описание 2-го поля записи;
        ...
        описание n-го поля записи;
    end;

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

    type goods = record
        name   : string[20];
        price  : real;
        number : integer;
    end;

    Переменные типа 'запись' описываются обычным образом. Можно задавать описание типа при описании переменной, создавать массивы из записей, записи из массивов, и т. д.

    var g1, g2  : goods;
        stock   : array [1 .. 100] of goods;
        student : record
            name  : string [30];
            group : byte;
            marks : array [1 .. 4] of byte;
        end;

    С записями целиком можно делать то же, что и с массивами: присваивать одну запись другой, если они одного типа, например:

    g1 := g2; g2 := stock[3];

    Все остальные действия выполняются с отдельными полями записи. Есть два способа доступа к полю записи: либо с помощью конструкции имя_записи.имя_поля, либо с использованием оператора присоединения with, например:

    g1.price := 200;
    with g1 do begin
        price := 200; number := 10
    end;

    Пример. Сведения о товарах на складе хранятся в текстовом файле. Для каждого товара отводится одна строка, в первых 20 позициях которой записано наименование товара, а затем через произвольное количество пробелов его цена и количество единиц. Программа по запросу выдает сведения о товаре или сообщение о том, что товар не найден ().

    program store;
    const Max_n = 100;
    type str20 = string [20];
         goods = record
            name   : str20;
            price  : real;
            number : integer;
         end;
    var stock     : array[1 .. Max_n] of goods;
        i, j, len : integer;
        name      : str20;
        found     : boolean;
        f         : text;
    begin
        assign(f, 'stock.txt'); reset(f); 
        i := 1;
        while not Eof(f) do begin
            with stock[i] do readln(f, name, price, number); 
            inc(i); 
            if i > Max_n then begin                                    { 1 }
                writeln('Переполнение массива'); exit end;
        end;
        while true do begin                                            { 2 }
            writeln('Введите наименование'); Readln(name);
            len := length(name);
            if len = 0 then break;                                     { 3 }
            for j := len + 1 to 20 do name := name + ' ';              { 4 }
            found := false;
            for j := 1 to i – 1 do begin                               { 5 }
                if name <> stock[j].name then continue; 
                with stock[j] do    writeln (name:22, price:7:2, number:5); 
                found := true;
                break;
            end;
            if not found then writeln ('Товар не найден');             { 6 }
        end;
    end.

    Инициализация записей выполняется в разделе констант, при этом для каждого поля задается его имя, после которого через двоеточие указывается значение.

    const g : goods = (name : 'boots'; price : 200; number : 10);

    Множества

    Множественный тип данных в языке Паскаль соответствует математическому представлению о множествах: это ограниченная совокупность различных элементов. Множество создается на основе элементов базового типа — это может быть перечисляемый тип, интервальный или byte. В множестве не может быть более 256 элементов, а их порядковые номера должны лежать в пределах от 0 до 255.

    Множество описывается с помощью служебных слов set of.

    type имя_типа = set of базовый_тип;

    Примеры описания множественных типов:

    type Caps   = set of 'A' .. 'Z';
        Colors  = set of (RED, GREEN, BLUE);
        Numbers = set of byte;

    Принадлежность переменных к множественному типу может быть определена прямо в разделе описания переменных, например:

    var oct : set of 0 .. 7;

    Тип 'множество' задает набор всех возможных подмножеств его элементов, включая пустое. Если базовый тип, на котором строится множество, имеет k элементов, то число подмножеств, входящих в это множество, равно 2k.

    Константы множественного типа записываются в виде заключенной в квадратные скобки последовательности элементов или интервалов базового типа, разделенных запятыми, например:

    ['A', 'D'] [1, 3, 6] [2, 3, 10 .. 13].

    Порядок перечисления элементов базового типа в константах не имеет значения. Константа вида [ ] означает пустое подмножество. Переменная типа 'множество' содержит одно конкретное подмножество значений множества. Пусть имеется переменная b интервального типа:

    var b : 1 .. 3; { переменная может принимать три различных значения: 1, 2 или 3 }

    Переменная m типа 'множество'

    var m : set of 1 .. 3;

    может принимать восемь различных значений:

    [ ] [1] [2] [3] [1, 2] [1, 3] [2, 3] [1, 2, 3]

    Операции над множествами

    Величины множественного типа не могут быть элементами списка ввода-вывода. Допустимые операции над множествами перечислены в табл. 3.1.

    Операции над множествами
    Знак Название Математическая запись Результат
    := Присваивание
    + Объединение $$\cup$$ Множество
    * Пересечение $$\cap$$ Множество
    Дополнение \ Множество
    = Тождественность = Логический
    <> Нетождественность $$\ne$$ Логический
    <= Содержится в $$\subset$$ Логический
    >= Содержит $$\supset$$ Логический
    in Принадлежность Логический

    Операции над множествами в основном соответствуют операциям, определенным в теории множеств.

    В операциях могут участвовать переменные и константы совместимых множественных типов. Исключение составляет операция in: ее первый операнд должен принадлежать базовому типу элементов множества, записанного вторым операндом.

    Рассмотрим примеры применения операций. Пусть задано множество, основанное на значениях прописных латинских букв.

    type Caps = set of 'A' .. 'Z';
    var a, b, c : Caps;
    begin
        a := ['A', 'U' .. 'Z'];
        b := [ 'M' .. 'Z'];
        c := a;                                                     { присваивание }
        c := a + b;                     { объединение, результат ['A', 'M' .. 'Z'] }
        c := a * b;                          { пересечение, результат ['U' .. 'Z'] }
        c := b – a;                            { вычитание, результат ['M' .. 'T'] }
        c := a – b;                                   { вычитание, результат ['A'] }
        if a = b then writeln ('тождественны');                    { не выполнится }
        if a <> b then writeln ('не тождественны');                   { выполнится }
        if c <= a then writeln ('c содержится в а');                  { выполнится }
        if 'N' in b then writeln ('в b есть N');                      { выполнится }
    end.

    С помощью констант-множеств часто проверяют, входит ли символ в заданный диапазон. Например, чтобы проверить, является ли введенный символ цифрой, можно написать:

    var c : char;
    ...
    if c in ['0' .. '9'] then ...

    Файлы

    Файловые типы данных введены в язык для работы с внешними устройствами — файлами на диске, портами, принтерами и т. д. Передача данных с внешнего устройства в оперативную память называется чтением, или вводом, обратный процесс — записью, или выводом.

    Файловые типы языка Паскаль бывают стандартные и определяемые программистом. Стандартными являются текстовый файл ( text ) и бестиповой файл ( file ). Они описываются в программе, например, так:

    var ft : text;
        fb : file;

    Программист может определить файл, состоящий из элементов определенного типа. Такой файл называется компонентным, или типизированным:

    var fc : file of <тип_компонент>;

    Компоненты могут быть любого типа, кроме файлового. Любой файл, в отличие от массива и записи, может содержать неограниченное количество элементов.

    Текстовые файлы предназначены для хранения информации в виде строк символов. При выводе в текстовый файл данные преобразуются из внутренней формы представления в символьную, понятную человеку, при вводе выполняется обратное преобразование.

    Бестиповые и компонентные файлы хранят данные в том же виде, в котором они представлены в оперативной памяти, то есть при обмене с файлом происходит побитовое копирование информации.

    Доступ к файлам может быть последовательным, когда очередной элемент можно прочитать (записать) только после аналогичной операции с предыдущим элементом, и прямым, при котором выполняется чтение (запись) произвольного элемента по заданному адресу. Текстовые файлы позволяют выполнять только последовательный доступ, в бестиповых и компонентных можно использовать оба метода.

    Прямой доступ возможен благодаря тому, что данные в этих файлах условно разделены на блоки одинакового размера, и перед операцией обмена выполняется установка текущей позиции файла на заданный блок. Прямой доступ в сочетании с отсутствием преобразований обеспечивает высокую скорость получения информации.

    Чтобы не путать файлы в программе и файлы на диске, переменные файлового типа называют логическими файлами, а реальные устройства и файлы на диске — физическими файлами. Их имена задаются с помощью строк символов, например:

  • 'primer.pas' — имя файла в текущем каталоге;
  • 'd:\pascal\input.txt' полное имя файла;
  • 'CON' 'NUL' 'COM1' 'PRN' — имена устройств.
  • Для организации ввода-вывода необходимо выполнить следующие действия.

  • Объявить файловую переменную.
  • Связать ее с физическим файлом.
  • Открыть файл для чтения и/или записи.
  • Выполнить операции ввода-вывода.
  • Закрыть файл.
  • Все стандартные процедуры и функции Паскаля, обеспечивающие ввод-вывод данных, работают только с логическими файлами. Ввод-вывод выполняется через буфер — специальную область оперативной памяти. Буфер выделяется для каждого открытого файла. При записи в файл вся информация сначала направляется в буфер и там накапливается до тех пор, пока весь буфер не заполнится. Только после этого или после специальной команды сброса происходит передача данных на внешнее устройство. При чтении из файла данные вначале считываются в буфер, причем данных считывается не столько, сколько запрашивается, а сколько поместится в буфер.

    Механизм буферизации позволяет более быстро и эффективно обмениваться информацией с внешними устройствами.

    В Паскале есть подпрограммы, применяемые для работы со всеми типами файлов: assign, close, erase, rename, reset, rewrite, eof и IOresult.

    Текстовые файлы

    Текстовый файл представляет собой последовательность строк символов переменной длины. Каждая строка заканчивается символами перевода строки и возврата каретки (их коды — 13 и 10). Эти символы вставляются в физический файл при нажатии клавиши Enter. При чтении файла эти символы не вводятся в переменные в программе, а воспринимаются как разделитель.

    Текстовый файл можно открыть не только для чтения или записи с помощью процедур reset и rewrite, но и для добавления информации в конец (процедура append ). Для чтения из текстового файла применяются процедуры read (f, список) и readln (f, [список]) . Они отличаются от процедур ввода с клавиатуры только наличием первого параметра — имени логического файла.

    Процедуры записи в текстовый файл — write (f, список) и writeln (f, [список]) . При записи в текстовый файл происходит преобразование из внутренней формы представления выводимых величин в символьные строки. Чтение и запись выполняются последовательно, то есть записать или считать очередной символ можно только после предыдущего.

    В Паскале есть несколько стандартных подпрограмм, которые предназначены только для работы с текстовыми файлами: flush, settextbuf, seekEof и seekEoln.

    Бестиповые файлы

    Бестиповые файлы предназначены для хранения участков оперативной памяти на внешних носителях. После описания файловой переменной

    var имя : file;

    ее требуется связать с физическим файлом с помощью процедуры assign. Чтение и запись производится через буфер 'порциями', равными размеру буфера. Размер буфера, отличающийся от стандартного (128 байт), можно задать с помощью второго параметра процедур reset и rewrite при открытии файла.

    reset(var f : file; bufsize : word)
    rewrite(var f : file; bufsize : word)

    Размер буфера должен находиться в пределах от 1 байта до 64 Кбайт.

    Собственно чтение и запись выполняются с помощью процедур blockread и blockwrite.

    blockread(var f : file; var x; count : word; var num : word);
    blockwrite(var f : file; var x; count : word; var num : word);

    Процедура blockread считывает в переменную x количество блоков count. Длина блока равна размеру буфера. Значение count должно быть больше или равно 1, за одно обращение нельзя ввести больше 64 Кбайт. При вводе никаких преобразований данных не выполняется. Необязательный параметр num возвращает количество прочитанных блоков.

    Процедура blockwrite записывает в файл количество блоков, равное count, начиная с адреса, заданного переменной x. Длина блока равна длине буфера. Необязательный параметр num возвращает число успешно записанных блоков.

    Для бестиповых файлов применяется как последовательный, так и прямой доступ.

    ).

    program create_bfile;
    var buf   : array[1 .. 4] of real;
        f_in  : text;
        f_out : file;
        i, k  : integer;
        name_in, name_out : string;
    begin
    {$I–}
        writeln('Введите имя входного файла'); readln(name_in);
        assign(f_in, name_in); reset(f_in);
        if IOResult <> 0 then begin
            writeln('Файл ', name_in,' не найден'); exit end;
        writeln('Введите имя выходного файла');    readln(name_out);
        assign(f_out, name_out); rewrite(f_out, sizeof(real) * 4);
    {$I+}
        i := 0;
        while not eof(f_in) do begin
            inc(i);
            read(f_in, buf[i]);
            if i = 4 then begin
                blockwrite(f_out, buf, 1); i := 0; end;
        end;
        if i <> 0 then begin
            for k := i + 1 to 4 do buf[k] := 0;
            blockwrite(f_out, buf, 1);
        end;
        close(f_in); close(f_out);
    end.

    Компонентные файлы

    Компонентные файлы применяются для хранения однотипных элементов в их внутренней форме представления. Тип компонент задается после ключевых слов file of.

    var имя : file of тип_компонент;

    Компоненты могут быть любого типа, кроме файлового, например вещественным числом или массивом записей. В операциях ввода-вывода могут участвовать только величины того же типа, что и компоненты файла, например:

    type mas = array [1 .. 100] of real;
    var a, b : mas;
        f    : file of mas;
    begin
        assign(f, 'some_file.dat'); rewrite(f);
        ...
        write(f, a, b);
        close(f)
    end.

    Обратите внимание, что компонентой этого файла является массив целиком. За одну операцию записывается или считывается столько компонент, сколько перечислено в процедурах write или read.

    Компонентные файлы, как и бестиповые, применяются не для просмотра их человеком, а для использования в программах.

    Прямой доступ

    Бестиповые и компонентные файлы состоят из блоков одинакового размера. В бестиповом файле размер блока равен длине буфера, а в компонентном — длине компоненты. Это позволяет применить к таким файлам прямой доступ, при котором операции выполняются с заданным блоком. Прямой доступ применяется только для физических файлов, расположенных на дисках.

    С помощью стандартной процедуры seek производится установка текущей позиции в файле на начало заданного блока, и следующая операция чтения-записи выполняется, начиная с этой позиции. Первый блок файла имеет номер 0.

    Ниже описаны стандартные подпрограммы для реализации прямого доступа.

    filepos (var f) : longint

    Функция возвращает текущую позицию в файле f. Для только что открытого файла это будет 0.

    filesize (var f) : longint

    Функция возвращает количество блоков в открытом файле f.

    seek (var f; n: longint)

    Процедура выполняет установку текущей позиции в открытом файле (позиционирование). В параметре n задается номер блока, к которому будет выполняться обращение.

    truncate (var f)

    Процедура устанавливает в текущей позиции признак конца файла и удаляет все последующие блоки.

    Пример. Программа, которая выводит на экран заданную по номеру запись из файла, сформированного в .

    program get_bfile;
    var buf      : array[1 .. 4] of real;
        f        : file;
        i, k     : integer;
        filename : string;
    begin
    {$I–}
        writeln('Введите имя входного файла'); readln(filename);
        assign(f, filename);
        reset(f, sizeof(real) * 4);
        if IOResult <> 0 then begin
            writeln('Файл ', filename, ' не найден'); exit end;
    {$I+}
        while true do begin    
            writeln('Введите номер записи или –1 для окончания'); 
            readln(k);
                if (k > filesize(f)) or (k < 0) then begin
                    writeln('Такой записи в файле нет',); exit end;
            seek(f, k);
            blockread(f, buf, 1);
            for i:= 1 to 4 do write(buf[i]:6:1);
        end;
        close(f);
    end.

    Таким же образом можно изменять заданную запись в файле. Файл при этом может быть открыт как для чтения, так и для записи. Попытка чтения-записи несуществующего блока приводит к ошибке времени выполнения.

    Совместимость типов

    Операнды в выражениях должны быть совместимых типов. Совместимость типов величин, участвующих в каждой операции, достигается при выполнении по крайней мере одного из следующих условий.

  • Оба типа одинаковые.
  • Оба типа вещественные.
  • Оба типа целочисленные.
  • Один тип является поддиапазоном другого.
  • Оба типа являются отрезками одного и того же основного типа.
  • Оба типа являются множественными типами с совместимыми базовыми типами.
  • Один тип является строковым типом, другой — строковым типом или типом pchar.
  • Один тип — pointer, другой — любой тип указателя.
  • Один тип — pchar, другой — символьный массив с нулевой базой вида array [0 .. X] of char (только при разрешении расширенного синтаксиса директивой {$X +}).
  • Оба типа являются указателями идентичных типов (только при разрешении расширенного синтаксиса директивой {$X+}).
  • Оба типа являются процедурными с идентичными типами результатов, одинаковым числом параметров и соответствием между параметрами.
  • Совместимость по присваиванию

    Этот вид совместимости требуется при присваивании значений, например в операторе присваивания или при передаче значений в подпрограмму.

    Значение типа T1 является совместимым по присваиванию с типом T2 (то есть допустим оператор T1 := T2 ), если выполняется одно из следующих условий.

  • T1 и T2 — тождественные типы (кроме файловых или типов, содержащих элементы файлового типа).
  • T1 и T2 — совместимые порядковые типы, при этом значения типа T2 попадают в диапазон возможных значений T1.
  • T1 и T2 — вещественные типы, при этом значения типа T2 попадают в диапазон возможных значений T1.
  • T1 — вещественный тип, а T2 — целочисленный.
  • T1 и T2 — строковые типы.
  • T1 — строковый тип, а T2 — символьный ( char ).
  • T1 и T2 — совместимые множественные типы, при этом все значения типа T2 попадают в диапазон возможных значений T1.
  • T1 и T2 — совместимые типы указателей.
  • T1 — тип pchar, а T2 — строковая константа (только при разрешении расширенного синтаксиса директивой {$X+} ).
  • T1 — тип pchar, а T2 символьный массив с нулевой базой вида array [0 .. n] of char (только при разрешении расширенного синтаксиса директивой {$X+}).
  • T1 и T2 — совместимые процедурные типы.
  • T1 представляет собой процедурный тип, а T2 — процедура или функция с идентичным типом результата и соответствующими параметрами.
  • На этапе компиляции и выполнения выдается сообщение об ошибке, если совместимость по присваиванию необходима, а ни одно из условий предыдущего списка не выполнено.

    Страницы:

    Презентацию к данной работе Вы можете скачать здесь.

    Информация, которую требуется обрабатывать в программе, имеет различную структуру. Для ее адекватного представления используются типы данных, которые программист определяет сам в разделе описания типов type. Типу дается произвольное имя, которое можно затем использовать для описания программных объектов точно так же, как и стандартные имена типов.

    type имя_типа = описание_типа
    ...
    var имя_переменной : имя_типа

    Можно задать тип и непосредственно при описании переменных:

    var имя_переменной : описание_типа

    Перечисляемый тип данных

    При написании программ часто возникает потребность определить несколько связанных между собой именованных констант, имеющих различные значения. Для этого удобно воспользоваться перечисляемым типом данных, все возможные значения которого задаются списком констант.

    type имя_типа = (список имен констант)

    Константы в списке перечисляются через запятую, например:

    type Menu = (READ, WRITE, EDIT, QUIT)

    Переменным перечисляемого типа можно присвоить либо значение одной из перечисленных констант, либо значение другой переменной того же типа, например:

    var m, n : Menu;
    …
    m := READ; n := m;

    Перечисляемый тип относится к порядковым типам данных. Константы в списке нумеруются с нуля. Например, Ord(READ) даст в результате 0, Succ(EDIT) QUIT. Попытка получения значения, следующего за последним, приведет к ошибке.

    Использовать перечисляемый тип в операциях ввода-вывода нельзя. Имена констант в пределах области их описания (программы или подпрограммы) должны быть уникальными.

    Интервальный тип данных

    С помощью интервального типа задается диапазон значений какого-либо типа.

    type имя_типа = константа_1 .. константа_2

    Константы должны быть одного и того же порядкового типа. Тип, на котором строится интервал, называется базовым. Константа_1 должна быть меньше или равна константе_2. Примеры описания интервальных типов:

    type Hour = 0 .. 23;
         Range = –100 .. 100;
         Letters = 'a' .. 'z';
         Actions = READ .. EDIT;

    Как и для других типов, определяемых программистом, интервальный тип можно задать прямо при описании переменной, например:

    var r : –100 .. 100;

    С переменной интервального типа можно делать все, что допустимо для ее базового типа. Ее значение должно находиться в указанном диапазоне, в противном случае произойдет ошибка времени выполнения 'Constant out of range'.

    Интервальный тип используется в программах как самостоятельно, так и внутри определения массива.

    Массивы

    При использовании простых переменных каждой области памяти, предназначенной для хранения какого-либо значения, соответствует свое имя. Если с группой величин одинакового типа требуется выполнять однообразные действия, им дают одно имя, а различают по порядковому номеру (индексу). Это позволяет компактно записывать множество операций с помощью циклов.

    Конечная именованная последовательность однотипных величин называется массивом. Чтобы описать массив, надо определить, какого типа его элементы и каким образом они пронумерованы (какого типа его индекс).

    type имя_типа = array [тип_индекса] of тип_элемента

    Здесь array и of — ключевые слова, тип индекса задается в квадратных скобках. Примеры описания типа:

    type  mas    = array [1 .. 10] of real;
          Color  = array [byte] of mas;
          Active = array [Menu] of boolean;

    В первом операторе описан тип массива из вещественных элементов, которые нумеруются от 1 до 10. Во втором операторе элементами массива являются массивы типа mas, а нумеруются они в пределах, допустимых для типа byte, то есть от 0 до 255. В третьей строке в качестве индекса использовано имя типа из раздела 'Перечисляемый тип данных', а сами элементы могут принимать значения true или false.

    Тип элементов массива может быть любым, кроме файлового, тип индексов — интервальным, перечисляемым или byte. Чаще всего для описания индекса используется интервальный тип данных.

    ВНИМАНИЕ Размещение массива в памяти происходит до выполнения программы, поэтому при описании индекса можно применять только константы или константные выражения.

    Обычно при описании массива верхняя граница его индекса задается в виде именованной константы, например:

    const n = 6;
    type intmas = array [1 .. n] of integer;

    После задания типа массива переменные этого типа описываются обычным образом:

    var a, b : intmas;

    С массивами в целом можно выполнять только одну операцию: присваивание. При этом массивы должны быть одного типа:

    b := a;

    Все остальные действия выполняются с отдельными элементами массива. Для обращения к элементу массива после имени массива указывается номер элемента в квадратных скобках:

    a[4] b[i]

    С элементом массива можно делать все, что допустимо для переменных того же типа.

    При обращении к элементу массива автоматический контроль выхода индекса за границу массива не производится. Для включения режима автоматического контроля необходимо добавить в любое место программы, предшествующее обращениям к элементу, ключ компиляции {$R+} или установить соответствующий режим в оболочке.

    Инициализация массивов.Элементам массива можно присвоить значения до начала выполнения программы. Это делается так же, как и для простых переменных, — в разделе описания констант, например:

    const a : intmas = (0, 5, –7, 100, 15, 1);

    Количество констант должно точно соответствовать числу элементов массива. Массивы, описанные в разделе var главной программы, обнуляются автоматически.

    Рассмотрим задачу поиска максимального элемента массива. Очевидно, что для отыскания самого большого элемента нужно сравнить между собой все элементы массива. Элементы выбираются попарно. При каждом сравнении из двух чисел выбирается наибольшее. Поскольку его надо где-то хранить, в программе описывается переменная того же типа, что и элементы массива. После окончания просмотра массива в ней окажется самый большой элемент. Перед началом просмотра в эту переменную заносится какой-либо элемент массива.

    Сформулируем алгоритм поиска максимума.

  • Принять за максимальный первый элемент массива.
  • Просмотреть массив, начиная со второго элемента.
  • Если очередной элемент оказывается больше максимального, принять его за максимальный.
  • Программа приведена в .

    program max_elem;
    const n = 20;
    var a   : array [1 .. n] of real;
        i   : integer;
        max : real;
    begin
        writeln('Введите ', n, ' элементов массива');
        for i := 1 to n do read(a[i]);
        max := a[1];                 { принять за максимальный первый элемент массива }
        for i := 2 to n do          { просмотреть массив, начиная со второго элемента }
            if a[i] > max then max := a[i];     { при необходимости обновить максимум }
        writeln('Максимальный элемент: ', max:6:2)
    end.

    Еще один простой пример работы с массивом приведен в .

    program sum_num;
    const n = 10;
    var a : array [1 .. n] of integer;
        i, sum, num : integer;
    begin
        writeln('Введите ', n, ' элементов массива');
        for i := 1 to n do read(a[i]);
        sum := 0;     
        num := 0; 
        for i := 1 to n do begin
            sum := sum + a[i];
            if a[i] < 0 then inc(num);
        end;
        writeln('Сумма элементов: ', sum);
        writeln('Отрицательных элементов: ', num);
    end.

    Двумерные массивы

    Элемент массива может быть любого типа, кроме файлового, следовательно, он может быть и массивом, например:

    const n = 4; m = 3;
    type mas  = array [1 .. n] of integer;    
         mas2 = array [1 .. m] of mas;

    Более компактно это можно записать так:

    type mas2 = array [1 .. m, 1 .. n] of integer;

    Здесь описан тип массива, состоящего из ). Обе размерности массива должны быть константами или константными выражениями. Имя типа указывается при описании переменных, например:

    var a, b : mas2;

    В памяти двумерный массив располагается по строкам.

    a11  a12  a13  a14  a21  a22   a23  a24  a31  a32  a33  a34
    |  – 1–я строка –   |   – 2–я строка –    |   – 3–я строка –   |

    Строки массива ничем не отделены одна от другой, то есть прямоугольной матрицей двумерный массив является только в нашем воображении. При просмотре массива от начала в первую очередь изменяется правый индекс (номер столбца).

    (рис 3.1) Матрица из m строк и n столбцов

    К элементу двумерного массива обращаются, указывая номер строки и номер столбца, на пересечении которых он расположен, например:

    a[1, 4] b[i, j] b[j, i]

    ВНИМАНИЕ Компилятор воспринимает как номер строки первый индекс, как бы он ни был обозначен в программе.

    При инициализации двумерных массивов каждая строка заключается в дополнительную пару круглых скобок, например:

    const a : mas2 = ( ( 2, 3, 1, 0),
                       ( 1, 9, 1, 3),
                       ( 3, 5, 7, 0)  );

    С массивами в целом определена только одна операция — присваивание массивов одного типа (например, b := a ) . Все остальные действия выполняются с отдельными элементами. Например, чтобы ввести с клавиатуры двумерный массив, необходимо организовать вложенные циклы:

    for i := 1 to m do
        for j := 1 to n do read(a[i, j]);

    В соответствии с приведенным здесь порядком следования циклов элементы массива должны вводиться по строкам (при этом неважно, как будут располагаться элементы массива, важен только порядок их следования).

    Пример. Программа, которая для целочисленной матрицы 3x4 определяет среднее арифметическое ее элементов и количество положительных элементов в каждой строке.

    Для нахождения среднего арифметического порядок перебора элементов массива (по строкам или по столбцам) роли не играет. Нахождение количества положительных элементов каждой строки требует просмотра матрицы по строкам ().

    program sred_n;
    const m = 3; n = 4;
    var a : array [1 .. m, 1 .. n] of integer;
        i, j, n_pos_el : integer;
        sred : real;
    begin
        for i := 1 to m do
        for j := 1 to n do read(a[i, j]);
        sred := 0;
        for i := 1 to m do begin
            n_pos_el := 0;
            for j := 1 to n do begin
                sred := sred + a[i, j];
                if a[i, j] > 0 then inc(n_pos_el);
            end;
            writeln('В ', i, '–й строке ', n_pos_el, ' положительных элементов');
        end;
        sred := sred / m / n;
        writeln('Среднее арифметическое: ', sred:6:2);
    end.
    СОВЕТ Записывайте операторы инициализации накапливаемых в цикле величин непосредственно перед циклом, в котором они вычисляются.

    Строки

    Строки используются для хранения последовательностей символов. В Паскале существует три типа строк:

  • стандартные ( string );
  • определяемые программистом на основе string ;
  • строки в динамической памяти
  • Строка типа string может содержать до 255 символов. Под каждый символ отводится по одному байту, в котором хранится код символа. Еще один байт отводится под фактическую длину строки. Таким образом, в памяти под одну переменную типа string всегда отводится 256 байт.

    Для коротких строк использовать стандартную строку неэффективно, поэтому есть возможность самостоятельно задавать максимальную длину строки, которая должна быть константой или константным выражением. Например, ниже описан собственный тип данных с именем str4:

    type str4 = string [4]; { переменная такого типа занимает в памяти 5 байтов }

    Примеры описания строк:

    const n = 15;
    var s : string;         { строка стандартого типа }
        s1 : str4;          { строка типа str4, описанного выше }
        s2 : string [n];    { описание типа задано при описании переменной }

    Инициализация строк, как и переменных других типов, выполняется в разделе описания констант.

    const s3 : string [15] = 'shooshpanchik';

    Внутреннее представление строки .

    (рис 3.2) Внутреннее представление строки s3

    Операции

    Строки можно присваивать друг другу. Если максимальная длина результирующей строки меньше длины исходной, лишние символы справа отбрасываются.

    s2 := 'shooshpanchik'; 
    s1 := s2;                  { в s1 будут помещены символы "shoo" }

    Строки можно склеивать (сцеплять) между собой с помощью операции конкатенации, которая обозначается знаком +, например:

    s1 := 'ком';
    s2 := s1 + 'пот';                        { результат — "компот" }

    Строки можно сравнивать друг с другом с помощью операций отношения. При сравнении строки рассматриваются посимвольно слева направо, при этом сравниваются коды соответствующих пар символов. Строки равны, если они имеют одинаковую длину и посимвольно эквивалентны. В строках разной длины существующий символ всегда больше соответствующего ему отсутствующего символа.

    'abc' > 'ab' 'abc' = 'abc' 'abc' < 'abc '

    Имя строки может использоваться в процедурах ввода-вывода:

    readln (s1, s2); write (s1);

    При вводе в строку считывается из входного потока количество символов, равное длине строки или меньшее, если символ перевода строки (который вводится нажатием клавиши Enter ) встретится раньше. При выводе под строку отводится количество позиций, равное ее фактической длине.

    К отдельному символу строки можно обращаться как к элементу массива символов, например s1[4] . Символ строки совместим с типом char, их можно использовать в выражениях одновременно, например:

    s1[4] := 'x'; writeln (s2[3] + s2[5] + 'r');

    Процедуры и функции для работы со строками

    При работе со строками, как правило, возникает необходимость выполнять их копирование, вставку, удаление или поиск. Для эффективной реализации этих действий в Паскале предусмотрены стандартные процедуры и функции. Они кратко описаны ниже.

    Функция Concat (s1, s2, ..., sn) возвращает строку, являющуюся слиянием строк s1, s2, ..., sn.

    Функция Copy (s, start, len) возвращает подстроку длиной len, начинающуюся с позиции start строки s.

    Процедура Delete (s, start, len) удаляет из строки s, начиная с позиции start, подстроку длиной len.

    Процедура Insert (subs, s, start) вставляет в строку s подстроку subs, начиная с позиции start.

    Функция Length (s) возвращает фактическую длину строки s, результат имеет тип byte.

    Функция Pos (subs, s) ищет вхождение подстроки subs в строку s и возвращает номер первого символа subs в s или нуль, если subs не содержится в s.

    Процедура Str (x, s) преобразует числовое значение x в строку s, при этом для x может быть задан формат, как в процедурах вывода write и writeln.

    Процедура Val (s, x, errcode) преобразует строку s в значение числовой переменной x, при этом строка s должна содержать символьное представление числа. В случае успешного преобразования переменная errcode равна нулю. Если же обнаружена ошибка, то errcode будет содержать номер позиции первого ошибочного символа, а значение x не определено.

    Пример. Программа читает текст из файла и выводит его на экран, заменяя заданную с клавиатуры последовательность символов на многоточии ().

    program censor;
    var s, str : string[10];
        f      : text;
        i, dl  : integer;
    begin
        assign(f, 'primer.txt'); reset(f);
        writeln('Какую последовательность заменять?'); readln(s);
        dl := length(s);
        while not Eof(f) do begin
            readln(f, str);
            i := 1;
            while i <> 0 do begin
                i := Pos(s, str);
                if i <> 0 then begin
                    Delete(str, i, dl); Insert('...', str, i);
                end;
            end;
            writeln(str);
        end;
        close(f)
    end.

    Записи

    В программах часто возникает необходимость логического объединения разнородных данных (например, база данных предприятия содержит для каждого сотрудника его фамилию, дату рождения, должность, оклад и т. д.). Для этого предназначен тип 'запись'. Он вводится с помощью ключевого слова record. Элементы записи называются полями.

    type имя_типа = record
        описание 1-го поля записи;
        описание 2-го поля записи;
        ...
        описание n-го поля записи;
    end;

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

    type goods = record
        name   : string[20];
        price  : real;
        number : integer;
    end;

    Переменные типа 'запись' описываются обычным образом. Можно задавать описание типа при описании переменной, создавать массивы из записей, записи из массивов, и т. д.

    var g1, g2  : goods;
        stock   : array [1 .. 100] of goods;
        student : record
            name  : string [30];
            group : byte;
            marks : array [1 .. 4] of byte;
        end;

    С записями целиком можно делать то же, что и с массивами: присваивать одну запись другой, если они одного типа, например:

    g1 := g2; g2 := stock[3];

    Все остальные действия выполняются с отдельными полями записи. Есть два способа доступа к полю записи: либо с помощью конструкции имя_записи.имя_поля, либо с использованием оператора присоединения with, например:

    g1.price := 200;
    with g1 do begin
        price := 200; number := 10
    end;

    Пример. Сведения о товарах на складе хранятся в текстовом файле. Для каждого товара отводится одна строка, в первых 20 позициях которой записано наименование товара, а затем через произвольное количество пробелов его цена и количество единиц. Программа по запросу выдает сведения о товаре или сообщение о том, что товар не найден ().

    program store;
    const Max_n = 100;
    type str20 = string [20];
         goods = record
            name   : str20;
            price  : real;
            number : integer;
         end;
    var stock     : array[1 .. Max_n] of goods;
        i, j, len : integer;
        name      : str20;
        found     : boolean;
        f         : text;
    begin
        assign(f, 'stock.txt'); reset(f); 
        i := 1;
        while not Eof(f) do begin
            with stock[i] do readln(f, name, price, number); 
            inc(i); 
            if i > Max_n then begin                                    { 1 }
                writeln('Переполнение массива'); exit end;
        end;
        while true do begin                                            { 2 }
            writeln('Введите наименование'); Readln(name);
            len := length(name);
            if len = 0 then break;                                     { 3 }
            for j := len + 1 to 20 do name := name + ' ';              { 4 }
            found := false;
            for j := 1 to i – 1 do begin                               { 5 }
                if name <> stock[j].name then continue; 
                with stock[j] do    writeln (name:22, price:7:2, number:5); 
                found := true;
                break;
            end;
            if not found then writeln ('Товар не найден');             { 6 }
        end;
    end.

    Инициализация записей выполняется в разделе констант, при этом для каждого поля задается его имя, после которого через двоеточие указывается значение.

    const g : goods = (name : 'boots'; price : 200; number : 10);

    Множества

    Множественный тип данных в языке Паскаль соответствует математическому представлению о множествах: это ограниченная совокупность различных элементов. Множество создается на основе элементов базового типа — это может быть перечисляемый тип, интервальный или byte. В множестве не может быть более 256 элементов, а их порядковые номера должны лежать в пределах от 0 до 255.

    Множество описывается с помощью служебных слов set of.

    type имя_типа = set of базовый_тип;

    Примеры описания множественных типов:

    type Caps   = set of 'A' .. 'Z';
        Colors  = set of (RED, GREEN, BLUE);
        Numbers = set of byte;

    Принадлежность переменных к множественному типу может быть определена прямо в разделе описания переменных, например:

    var oct : set of 0 .. 7;

    Тип 'множество' задает набор всех возможных подмножеств его элементов, включая пустое. Если базовый тип, на котором строится множество, имеет k элементов, то число подмножеств, входящих в это множество, равно 2k.

    Константы множественного типа записываются в виде заключенной в квадратные скобки последовательности элементов или интервалов базового типа, разделенных запятыми, например:

    ['A', 'D'] [1, 3, 6] [2, 3, 10 .. 13].

    Порядок перечисления элементов базового типа в константах не имеет значения. Константа вида [ ] означает пустое подмножество. Переменная типа 'множество' содержит одно конкретное подмножество значений множества. Пусть имеется переменная b интервального типа:

    var b : 1 .. 3; { переменная может принимать три различных значения: 1, 2 или 3 }

    Переменная m типа 'множество'

    var m : set of 1 .. 3;

    может принимать восемь различных значений:

    [ ] [1] [2] [3] [1, 2] [1, 3] [2, 3] [1, 2, 3]

    Операции над множествами

    Величины множественного типа не могут быть элементами списка ввода-вывода. Допустимые операции над множествами перечислены в табл. 3.1.

    Операции над множествами
    Знак Название Математическая запись Результат
    := Присваивание
    + Объединение $$\cup$$ Множество
    * Пересечение $$\cap$$ Множество
    Дополнение \ Множество
    = Тождественность = Логический
    <> Нетождественность $$\ne$$ Логический
    <= Содержится в $$\subset$$ Логический
    >= Содержит $$\supset$$ Логический
    in Принадлежность Логический

    Операции над множествами в основном соответствуют операциям, определенным в теории множеств.

    В операциях могут участвовать переменные и константы совместимых множественных типов. Исключение составляет операция in: ее первый операнд должен принадлежать базовому типу элементов множества, записанного вторым операндом.

    Рассмотрим примеры применения операций. Пусть задано множество, основанное на значениях прописных латинских букв.

    type Caps = set of 'A' .. 'Z';
    var a, b, c : Caps;
    begin
        a := ['A', 'U' .. 'Z'];
        b := [ 'M' .. 'Z'];
        c := a;                                                     { присваивание }
        c := a + b;                     { объединение, результат ['A', 'M' .. 'Z'] }
        c := a * b;                          { пересечение, результат ['U' .. 'Z'] }
        c := b – a;                            { вычитание, результат ['M' .. 'T'] }
        c := a – b;                                   { вычитание, результат ['A'] }
        if a = b then writeln ('тождественны');                    { не выполнится }
        if a <> b then writeln ('не тождественны');                   { выполнится }
        if c <= a then writeln ('c содержится в а');                  { выполнится }
        if 'N' in b then writeln ('в b есть N');                      { выполнится }
    end.

    С помощью констант-множеств часто проверяют, входит ли символ в заданный диапазон. Например, чтобы проверить, является ли введенный символ цифрой, можно написать:

    var c : char;
    ...
    if c in ['0' .. '9'] then ...

    Файлы

    Файловые типы данных введены в язык для работы с внешними устройствами — файлами на диске, портами, принтерами и т. д. Передача данных с внешнего устройства в оперативную память называется чтением, или вводом, обратный процесс — записью, или выводом.

    Файловые типы языка Паскаль бывают стандартные и определяемые программистом. Стандартными являются текстовый файл ( text ) и бестиповой файл ( file ). Они описываются в программе, например, так:

    var ft : text;
        fb : file;

    Программист может определить файл, состоящий из элементов определенного типа. Такой файл называется компонентным, или типизированным:

    var fc : file of <тип_компонент>;

    Компоненты могут быть любого типа, кроме файлового. Любой файл, в отличие от массива и записи, может содержать неограниченное количество элементов.

    Текстовые файлы предназначены для хранения информации в виде строк символов. При выводе в текстовый файл данные преобразуются из внутренней формы представления в символьную, понятную человеку, при вводе выполняется обратное преобразование.

    Бестиповые и компонентные файлы хранят данные в том же виде, в котором они представлены в оперативной памяти, то есть при обмене с файлом происходит побитовое копирование информации.

    Доступ к файлам может быть последовательным, когда очередной элемент можно прочитать (записать) только после аналогичной операции с предыдущим элементом, и прямым, при котором выполняется чтение (запись) произвольного элемента по заданному адресу. Текстовые файлы позволяют выполнять только последовательный доступ, в бестиповых и компонентных можно использовать оба метода.

    Прямой доступ возможен благодаря тому, что данные в этих файлах условно разделены на блоки одинакового размера, и перед операцией обмена выполняется установка текущей позиции файла на заданный блок. Прямой доступ в сочетании с отсутствием преобразований обеспечивает высокую скорость получения информации.

    Чтобы не путать файлы в программе и файлы на диске, переменные файлового типа называют логическими файлами, а реальные устройства и файлы на диске — физическими файлами. Их имена задаются с помощью строк символов, например:

  • 'primer.pas' — имя файла в текущем каталоге;
  • 'd:\pascal\input.txt' полное имя файла;
  • 'CON' 'NUL' 'COM1' 'PRN' — имена устройств.
  • Для организации ввода-вывода необходимо выполнить следующие действия.

  • Объявить файловую переменную.
  • Связать ее с физическим файлом.
  • Открыть файл для чтения и/или записи.
  • Выполнить операции ввода-вывода.
  • Закрыть файл.
  • Все стандартные процедуры и функции Паскаля, обеспечивающие ввод-вывод данных, работают только с логическими файлами. Ввод-вывод выполняется через буфер — специальную область оперативной памяти. Буфер выделяется для каждого открытого файла. При записи в файл вся информация сначала направляется в буфер и там накапливается до тех пор, пока весь буфер не заполнится. Только после этого или после специальной команды сброса происходит передача данных на внешнее устройство. При чтении из файла данные вначале считываются в буфер, причем данных считывается не столько, сколько запрашивается, а сколько поместится в буфер.

    Механизм буферизации позволяет более быстро и эффективно обмениваться информацией с внешними устройствами.

    В Паскале есть подпрограммы, применяемые для работы со всеми типами файлов: assign, close, erase, rename, reset, rewrite, eof и IOresult.

    Текстовые файлы

    Текстовый файл представляет собой последовательность строк символов переменной длины. Каждая строка заканчивается символами перевода строки и возврата каретки (их коды — 13 и 10). Эти символы вставляются в физический файл при нажатии клавиши Enter. При чтении файла эти символы не вводятся в переменные в программе, а воспринимаются как разделитель.

    Текстовый файл можно открыть не только для чтения или записи с помощью процедур reset и rewrite, но и для добавления информации в конец (процедура append ). Для чтения из текстового файла применяются процедуры read (f, список) и readln (f, [список]) . Они отличаются от процедур ввода с клавиатуры только наличием первого параметра — имени логического файла.

    Процедуры записи в текстовый файл — write (f, список) и writeln (f, [список]) . При записи в текстовый файл происходит преобразование из внутренней формы представления выводимых величин в символьные строки. Чтение и запись выполняются последовательно, то есть записать или считать очередной символ можно только после предыдущего.

    В Паскале есть несколько стандартных подпрограмм, которые предназначены только для работы с текстовыми файлами: flush, settextbuf, seekEof и seekEoln.

    Бестиповые файлы

    Бестиповые файлы предназначены для хранения участков оперативной памяти на внешних носителях. После описания файловой переменной

    var имя : file;

    ее требуется связать с физическим файлом с помощью процедуры assign. Чтение и запись производится через буфер 'порциями', равными размеру буфера. Размер буфера, отличающийся от стандартного (128 байт), можно задать с помощью второго параметра процедур reset и rewrite при открытии файла.

    reset(var f : file; bufsize : word)
    rewrite(var f : file; bufsize : word)

    Размер буфера должен находиться в пределах от 1 байта до 64 Кбайт.

    Собственно чтение и запись выполняются с помощью процедур blockread и blockwrite.

    blockread(var f : file; var x; count : word; var num : word);
    blockwrite(var f : file; var x; count : word; var num : word);

    Процедура blockread считывает в переменную x количество блоков count. Длина блока равна размеру буфера. Значение count должно быть больше или равно 1, за одно обращение нельзя ввести больше 64 Кбайт. При вводе никаких преобразований данных не выполняется. Необязательный параметр num возвращает количество прочитанных блоков.

    Процедура blockwrite записывает в файл количество блоков, равное count, начиная с адреса, заданного переменной x. Длина блока равна длине буфера. Необязательный параметр num возвращает число успешно записанных блоков.

    Для бестиповых файлов применяется как последовательный, так и прямой доступ.

    ).

    program create_bfile;
    var buf   : array[1 .. 4] of real;
        f_in  : text;
        f_out : file;
        i, k  : integer;
        name_in, name_out : string;
    begin
    {$I–}
        writeln('Введите имя входного файла'); readln(name_in);
        assign(f_in, name_in); reset(f_in);
        if IOResult <> 0 then begin
            writeln('Файл ', name_in,' не найден'); exit end;
        writeln('Введите имя выходного файла');    readln(name_out);
        assign(f_out, name_out); rewrite(f_out, sizeof(real) * 4);
    {$I+}
        i := 0;
        while not eof(f_in) do begin
            inc(i);
            read(f_in, buf[i]);
            if i = 4 then begin
                blockwrite(f_out, buf, 1); i := 0; end;
        end;
        if i <> 0 then begin
            for k := i + 1 to 4 do buf[k] := 0;
            blockwrite(f_out, buf, 1);
        end;
        close(f_in); close(f_out);
    end.

    Компонентные файлы

    Компонентные файлы применяются для хранения однотипных элементов в их внутренней форме представления. Тип компонент задается после ключевых слов file of.

    var имя : file of тип_компонент;

    Компоненты могут быть любого типа, кроме файлового, например вещественным числом или массивом записей. В операциях ввода-вывода могут участвовать только величины того же типа, что и компоненты файла, например:

    type mas = array [1 .. 100] of real;
    var a, b : mas;
        f    : file of mas;
    begin
        assign(f, 'some_file.dat'); rewrite(f);
        ...
        write(f, a, b);
        close(f)
    end.

    Обратите внимание, что компонентой этого файла является массив целиком. За одну операцию записывается или считывается столько компонент, сколько перечислено в процедурах write или read.

    Компонентные файлы, как и бестиповые, применяются не для просмотра их человеком, а для использования в программах.

    Прямой доступ

    Бестиповые и компонентные файлы состоят из блоков одинакового размера. В бестиповом файле размер блока равен длине буфера, а в компонентном — длине компоненты. Это позволяет применить к таким файлам прямой доступ, при котором операции выполняются с заданным блоком. Прямой доступ применяется только для физических файлов, расположенных на дисках.

    С помощью стандартной процедуры seek производится установка текущей позиции в файле на начало заданного блока, и следующая операция чтения-записи выполняется, начиная с этой позиции. Первый блок файла имеет номер 0.

    Ниже описаны стандартные подпрограммы для реализации прямого доступа.

    filepos (var f) : longint

    Функция возвращает текущую позицию в файле f. Для только что открытого файла это будет 0.

    filesize (var f) : longint

    Функция возвращает количество блоков в открытом файле f.

    seek (var f; n: longint)

    Процедура выполняет установку текущей позиции в открытом файле (позиционирование). В параметре n задается номер блока, к которому будет выполняться обращение.

    truncate (var f)

    Процедура устанавливает в текущей позиции признак конца файла и удаляет все последующие блоки.

    Пример. Программа, которая выводит на экран заданную по номеру запись из файла, сформированного в .

    program get_bfile;
    var buf      : array[1 .. 4] of real;
        f        : file;
        i, k     : integer;
        filename : string;
    begin
    {$I–}
        writeln('Введите имя входного файла'); readln(filename);
        assign(f, filename);
        reset(f, sizeof(real) * 4);
        if IOResult <> 0 then begin
            writeln('Файл ', filename, ' не найден'); exit end;
    {$I+}
        while true do begin    
            writeln('Введите номер записи или –1 для окончания'); 
            readln(k);
                if (k > filesize(f)) or (k < 0) then begin
                    writeln('Такой записи в файле нет',); exit end;
            seek(f, k);
            blockread(f, buf, 1);
            for i:= 1 to 4 do write(buf[i]:6:1);
        end;
        close(f);
    end.

    Таким же образом можно изменять заданную запись в файле. Файл при этом может быть открыт как для чтения, так и для записи. Попытка чтения-записи несуществующего блока приводит к ошибке времени выполнения.

    Совместимость типов

    Операнды в выражениях должны быть совместимых типов. Совместимость типов величин, участвующих в каждой операции, достигается при выполнении по крайней мере одного из следующих условий.

  • Оба типа одинаковые.
  • Оба типа вещественные.
  • Оба типа целочисленные.
  • Один тип является поддиапазоном другого.
  • Оба типа являются отрезками одного и того же основного типа.
  • Оба типа являются множественными типами с совместимыми базовыми типами.
  • Один тип является строковым типом, другой — строковым типом или типом pchar.
  • Один тип — pointer, другой — любой тип указателя.
  • Один тип — pchar, другой — символьный массив с нулевой базой вида array [0 .. X] of char (только при разрешении расширенного синтаксиса директивой {$X +}).
  • Оба типа являются указателями идентичных типов (только при разрешении расширенного синтаксиса директивой {$X+}).
  • Оба типа являются процедурными с идентичными типами результатов, одинаковым числом параметров и соответствием между параметрами.
  • Совместимость по присваиванию

    Этот вид совместимости требуется при присваивании значений, например в операторе присваивания или при передаче значений в подпрограмму.

    Значение типа T1 является совместимым по присваиванию с типом T2 (то есть допустим оператор T1 := T2 ), если выполняется одно из следующих условий.

  • T1 и T2 — тождественные типы (кроме файловых или типов, содержащих элементы файлового типа).
  • T1 и T2 — совместимые порядковые типы, при этом значения типа T2 попадают в диапазон возможных значений T1.
  • T1 и T2 — вещественные типы, при этом значения типа T2 попадают в диапазон возможных значений T1.
  • T1 — вещественный тип, а T2 — целочисленный.
  • T1 и T2 — строковые типы.
  • T1 — строковый тип, а T2 — символьный ( char ).
  • T1 и T2 — совместимые множественные типы, при этом все значения типа T2 попадают в диапазон возможных значений T1.
  • T1 и T2 — совместимые типы указателей.
  • T1 — тип pchar, а T2 — строковая константа (только при разрешении расширенного синтаксиса директивой {$X+} ).
  • T1 — тип pchar, а T2 символьный массив с нулевой базой вида array [0 .. n] of char (только при разрешении расширенного синтаксиса директивой {$X+}).
  • T1 и T2 — совместимые процедурные типы.
  • T1 представляет собой процедурный тип, а T2 — процедура или функция с идентичным типом результата и соответствующими параметрами.
  • На этапе компиляции и выполнения выдается сообщение об ошибке, если совместимость по присваиванию необходима, а ни одно из условий предыдущего списка не выполнено.

    Вернуться к учебному плану