Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Информатика_ч2-записи

.pdf
Скачиваний:
4
Добавлен:
15.04.2015
Размер:
417.35 Кб
Скачать

21

END;

CLOSE(FS);

IF N > -1 THEN BEGIN

{ЗАПОЛНЕНИЕ МАССИВА ИНДЕКСОВ MI} FOR I := 0 TO N

DO BEGIN MI[I].FIO := MS[I].FIO; MI[I].NAMBER := I END;

{СОРТИРОВКА ИНДЕКСНОГО МАССИВА ПО КЛЮЧЕВОМУ ПОЛЮ

FIO}

FOR I := 0 TO N

DO FOR J := N DOWNTO I+1 DO IF MI[J-1].FIO > MI[J].FIO THEN BEGIN

L := MI[J-1]; MI[J-1] := MI[J]; MI[J] := L END;

{ФОРМИРОВАНИЕ ИНДЕКСНОГО ФАЙЛА} WRITELN('ЖДИТЕ ИДЕТ ИНДЕКСАЦИЯ ФАЙЛА ...',STI); ASSIGN(FI, STI); REWRITE(FI);

FOR I := 0 TO N

DO WRITE(FI, MI[I]); CLOSE(FI)

END;

END; {INDEX} BEGIN

WRITE('ВВЕДИТЕ ИМЯ ОСНОВНОГО ФАЙЛА: '); READLN(ST); ASSIGN (FS, ST); RESET(FS);

WRITE('ВВЕДИТЕ ИМЯ ИНДЕКСНОГО ФАЙЛА: '); READLN(STI); ASSIGN (FI, STI); RESET(FI);

FL := FALSE;

WRITE('ВВЕДИТЕ ФАМИЛИЮ СТУДЕНТА: '); READLN(STUD);

WHILE NOT EOF(FI) {ПРОСМОТР ИНДЕКСНОГО ФАЙЛА} DO BEGIN

READ(FI, L);

IF L.FIO = STUD {Найдена запись, подлежащая коррекции} THEN BEGIN

SEEK(FS, L.NAMBER); READ(FS, S); {Чтение записи из файла}

WRITE(S.TAB, '=> '); READLN(S.TAB); {Коррекция полей записи} WRITE(S.FIO, '=> '); READLN(S.FIO);

WRITE(S.DATA, '=> '); READLN(S.DATA);

WRITE(S.GRUP, '=> '); READLN(S.GRUP);

WRITE(S.STEPA, '=> '); READLN(S.STEPA);

IF S.FIO <> STUD THEN FL := TRUE;

{Ключевое поле FIO изменено}

SEEK(FS, L.NAMBER); WRITE(FS, S)

{Сохранение изменений в файле}

END

 

END;

 

22

CLOSE(FS); CLOSE(FI);

{ПРИ ИЗМЕНЕНИИ КЛЮЧЕВОГО ПОЛЯ ИДЕТ ПЕРЕИНДЕКСАЦИЯ} IF FL THEN IND;

END.

Изменение структуры основного файла

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

Пример 124. Требуется во-первых, сохранив всю хранимую информацию, изменить в файле STUDENTS.DAT поле FIO: STRING[20], сократив его размер на 5 байт, то есть на FIO: STRING[15]. Во-вторых, добавить два новых поля: DEL: BYTE и DATE: STRING[8].

Алгоритм функционирования программы следующий: Присвоить основному файлу имя (процедура Assign); Открыть уже существующий на диске файл (процедура Reset); Присвоить рабочему файлу имя (процедура Assign);

Создать рабочий файл TEMP на диске A: (процедура Rewrite);

Читать основной файл запись за записью. Каждую запись копировать в соответствующую компоненту рабочего файла. Новые поля DEL и DATE заполнять величинами - $0 и пробел, соответственно. Сформированную таким образом запись писать в файл TEMP.

Закрыть основной файл (процедура Close); Удалить основной файл (процедура Erase); Закрыть рабочий файл TEMP (процедура Close);

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

PROGRAM PR124;

TYPE OLD = RECORD

TAB : LONGINT;

FIO : STRING[20];

DATA : STRING[8];

GRUP : STRING[7]; STEPA: REAL END;

NEW = RECORD

TAB: LONGINT;

FIO : STRING[15]; {МЕНЯЕМ РАЗМЕР ПОЛЯ} DATA : STRING[8];

GRUP : STRING[7];

STEPA: REAL;

 

DEL: BYTE;

{ДОБАВЛЯЕМ НОВЫЕ ПОЛЯ DEL И DATE}

DATE: STRING[8]

 

END;

 

23

VAR S: OLD; N: NEW; ST: STRING; FS: FILE OF OLD; FI: FILE OF NEW;

BEGIN

WRITELN('ВВЕДИТЕ ИМЯ ОСНОВНОГО ФАЙЛА: '); READLN(ST); ASSIGN (FS, ST); RESET(FS);

ASSIGN (FI, 'A:\TEMP'); REWRITE(FI); {СОЗДАНИЕ РАБОЧЕГО НАБОРА}

WHILE NOT EOF(FS)

{ПРОСМОТР ОСНОВНОГО ФАЙЛА}

DO BEGIN

 

READ(FS, S);

N.TAB := S.TAB; N.FIO := S.FIO; {КОПИРОВАНИЕ ИНФОРМАЦИИ} N.DATA := S.DATA; N.GRUP := S.GRUP; N.STEPA := S.STEPA;

N.DEL := $0; N.DATE := ' '; WRITE(FI, N);

END;

CLOSE(FS);

ERASE(FS); {УДАЛЕНИЕ ОСНОВНОГО ФАЙЛА} CLOSE(FI);

RENAME(FI, ST); {ПЕРЕИМЕНОВАНИЕ РАБОЧЕГО НАБОРА В ОСНОВНОЙ ФАЙЛ}

END.

Использование клавиш для управлением программой

Работа с файлом предусматривает ряд альтернативных процессов (действий). Инициировать эти процессы можно с помощью активных клавиш. Например, для удаления записи можно использовать клавишу Del , для перемещения к предыдущей записи – стрелка вверх, для перемещения к первой записи - Home , для выхода из программы – клавишу Esc .

Работа с клавишами, в том числе и управляющими Shift , Ctrl , Alt - осуществляется с помощью функции ReadKey. При этом считываемый символ не отображается на экране. Если перед вызовом ReadKey функция KeyPressed имеет значение True, то символ считывается немедленно. В противном случае, функция ожидает нажатия клавиши. Специальные клавиши на PC клавиатуре генерируют расширенные коды сканирования. (Расширенные коды сканирования приведены в табл. 50) К специальным клавишам относятся функциональные клавиши, клавиши управления курсором, клавиши Alt и т.п.

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

Пример 125. Разработать программу, которая с помощью функции READKEY отображает на экране ASC II код, нажатой клавиши, а для служебных клавиш или их комбинаций с другими клавишами второй (расширенный) код.

PROGRAM PR125; USES CRT;

Var CH, CH1:CHAR;

24

Begin

Writeln('Нажимайте клавиши, или их комбинации и смотрите коды'); Writeln('Клавиша ESC - конец');

While TRUE {БЕСКОНЕЧНЫЙ ЦИКЛ} Do begin

CH := READKEY; {Ожидание нажатия клавиши} IF ORD(CH) = 0

THEN BEGIN CH1:=READKEY; {КЛАВИШИ ВТОРОЙ ГРУППЫ} WRITELN('КОД КЛАВИШИ -> 0, РАСШИРЕНИЕ -> ', ORD(CH1)) END

ELSE BEGIN {КЛАВИШИ ПЕРВОЙ ГРУППЫ} WRITELN('КОД КЛАВИШИ - ', ORD(CH));

IF ORD(CH) = 27 THEN BREAK {ВЫХОД ИЗ ЦИКЛА WHILE по клавише

Esc}

END

END {WHILE} END.

В программе PR125 активной является только клавиша Esc, потому, что только ее код обрабатывается программным путем и влияет на ход вычислительного процесса. Остальные клавиши пассивно фиксируются (после нажатия их коды высвечиваются на экране монитора). С помощью программы PR125 можно получить табл. 50.

Таблица 50.

Возвращаемые коды

 

Клавиши

Первый

Второй

 

13

-

 

Enter

27

-

 

Esc

32

-

 

Пробел

0

3

 

Ctrl + C (Null – нулевой символ)

0

15

 

Shift + Tab

0

16

- 25

Alt + Q/W/E/R/T/Y/0/I/O/P

0

30

- 38

Alt + A/S/D/F/e/H/J/K/L

0

44

- 50

Alt + Z/X/C/У/В/М/М

0

59

- 68

Ключи F1 – F10

0

71

 

Home (начало)

0

72

 

Стрелка вверх

0

73

 

PgUp (страница в верх)

0

75

 

Стрелка влево

0

77

 

Стрелка вправо

0

79

 

End (конец)

0

80

 

Стрелка вниз

0

81

 

PgDn (страница в низ)

0

82

 

Ins (вставка)

0

83

 

Del (удалить)

0

84

- 93

Shift + F1 / … / Shift + F10

0

94

- 103

Ctrl + F1 / … / Ctrl + F10

0

104 - 113

Alt + F1 / … / Alt + F10

0

114

Ctrl + PrtScr (копия с экрана)

0

115

Ctrl + Стрелка влево

 

 

 

 

25

 

 

0

 

116

 

Ctrl + Стрелка вправо

 

 

 

 

 

 

0

 

117

 

Ctrl + End (конец)

 

 

0

 

118

 

Ctrl + PgDn (страница в низ)

 

 

0

 

119

 

Ctrl + Home (начало)

 

 

0

 

120 - 131

 

Alt + 1/2/3/4/5/6/7/8/9/0/-/=

 

 

0

 

132

 

Ctrl + PgUp (страница в верх)

 

Удаление записи из файла

Удаление записей происходит в два этапа. На первом этапе запись помечается как удаляемая. Для этого в записи предусматривается специальное поле DEL: BYTE, занимающее один байт памяти. При пометке записи на удаление в нее заносится специальный код, например, $FF. Таким образом, удаленные записи физически продолжают существовать еще какое-то время. Как правило, при просмотре файла они высвечиваются с пометкой Delete. Если пользователь базы данных обнаружил, что напрасно хочет удалить ту или иную запись он может ее восстановить, повторной пометкой записи на удаление.

На втором этапе происходит физическое удаление записей помеченных как удаляемые. Этот процесс называется сжатием файла. Создается рабочий файл TEMP структура которого тождественна сжимаемому. Основной файл просматривается с первой записи до последней. Каждая запись основного файла проверяется на признак “удалена” DEL = $FF. Если это условие не выполнено, то текущая запись копируется в файл TEMP. Таким образом, когда мы достигнем метки EOF основного файла все непомеченные на удаление записи этого файла будут скопированы в рабочий файл. Теперь следует удалить основной файл с помощью процедуры Erase. А файлу TEMP присвоить имя основного файла с помощью процедуры Rename.

Пример 126. Требуется разработать программу, обеспечивающую последовательный просмотр и пометку удаляемых записей в файле STUDENTS.DAT с помощью поля - DEL: BYTE, а также сжатие файла, если это нужно пользователю.

PROGRAM PR126; USES CRT;

Type STUDENT = record tab : Longint;

Fio : String[15];

Data : String[8];

Grup : String[7]; Stepa: Real; DEL: BYTE;

DATE: STRING[8] end; {RECORD}

Var S:STUDENT; St: String;CH, CH1: CHAR;

Fs, Fi: File of STUDENT; NZ: WORD; FUNCTION DL(X: BYTE): STRING; BEGIN

DL := ' ';

IF X = $FF THEN DL := 'DELETE' END; {FUNCTION}

26

Begin

Writeln('Введите имя основного файла: '); Readln(St); Assign (Fs, St); Reset(Fs); { основного файла} WRITELN('Используйте клавиши:');

WRITELN('ESC - завершение работы с записями файла;'); WRITELN('ENTER - переход к следующей записи;');

WRITELN('DEL - пометка записи на удаление, отмена признака удаления;'); WRITELN('Пробел - повторный вывод записи;');

WRITELN('HOME - первая запись в файле;'); WRITELN('Стрелка вверх - предыдущая запись.');

While Not Eof(Fs) {Просмотр основного файла} Do begin

NZ := FILEPOS(Fs); {Запомнить номер текущей записи} Read(Fs, S);

WRITELN(NZ:2, S.TAB:9, S.FIO:17, S.Data:10, S.Grup:9, S.STEPA:9:2,

DL(S.DEL):8);

 

CH:=' ';CH1:=' ';

 

CH := READKEY;

{Ожидание нажатия клавиши}

IF ORD(CH) = 0

 

THEN BEGIN CH1:=READKEY;{КЛАВИШИ ВТОРОЙ ГРУППЫ} CASE ORD(CH1) of

83: BEGIN

IF S.DEL = $FF {НАЖАТА КЛАВИША: DEL}

THEN S.DEL:= $00 {ОТМЕНА УДПЕНИЯ}

ELSE S.DEL:= $FF; {ПОМЕТКА ЗАПИСИ НА УДАЛЕНИЕ}

SEEK(Fs, NZ);

 

WRITE(FS,S)

{ЗАПИСЬ ОТМЕТКИ В ФАЙЛ}

END;

 

72: IF NZ=0 {НАЖАТА КЛАВИША: Стрелка вверх}

THEN SEEK(Fs, NZ) {ПЕРВАЯ ЗАПИСЬ }

ELSE SEEK(Fs, NZ-1);{НА ПРЕДЫДУЩУЮ ЗАПИСЬ }

71: SEEK(Fs,0)

{НАЖАТА КЛАВИША: HOME }

END {CASE}

 

END {THEN}

 

ELSE CASE ORD(CH) of {КЛАВИШИ ПЕРВОЙ ГРУППЫ}

13: CONTINUE;

{НАЖАТА КЛАВИША: ENTER}

27: BREAK;

{НАЖАТА КЛАВИША: ESC }

32: SEEK(Fs,NZ)

{НАЖАТА КЛАВИША: ПРОБЕЛ}

END; {CASE}

END; {WHILE}

WRITELN ('БУДЕТЕ СЖИМАТЬ ФАЙЛ: Y - ДА'); READLN(CH);

IF (CH = 'y') OR (CH = 'Y')

THEN BEGIN

Assign (Fi, 'A:\TEMP'); Rewrite(Fi); {Создание рабочего набора}

SEEK(Fs,0);

27

While Not Eof(FS) {Просмотр основного файла} Do begin

Read(Fs, S);

IF S.DEL <> $FF THEN WRITE(FI, S) {ПРОПУСК ПОМЕЧЕННЫХ ЗАПИСЕЙ}

END;

Close(Fs);

ERASE(Fs); {Удаление основного файла} Close(Fi);

RENAME(Fi, St) {Переименование рабочего набора в основной файл} END

END.

Добавление записи в файл

Новые записи, как правило, дописываются в конец файла. Этот режим часто называют расширением файла. Для реализации этого режима файл открывается с помощью процедур Assign и Reset. Далее определяется количество компонент в файле с помощью процедуры FILESIZE и указатель файла смещается с помощью процедуры Seek за последний компонент файла (на метку EOF). Далее осуществляется ввод информации в новую запись, которая помещается в файл с помощью процедуры WRITE. После записи последней компоненты файл закрывается с помощью процедуры Close.

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

PROGRAM PR127;

USES CRT, DOS;

Type STUDENT = record tab : Longint;

Fio : String[15];

Data : String[8];

Grup : String[7]; Stepa: Real; DEL: BYTE;

DATE: STRING[8] end; {RECORD}

Var S:STUDENT; St: String; CH, CH1:CHAR;

Fs, Fi: File of STUDENT; NZ:WORD;

 

FUNCTION NOW: STRING;

{ Формирование даты }

VAR YEAR, MONTH, DAY, DAYOFWEEK: WORD; GG, MM, DD: STRING[2]; ST: STRING[4];

BEGIN

GETDATE(YEAR, MONTH, DAY, DAYOFWEEK);

STR(YEAR:4, ST); GG := ST[3] + ST[4]; STR(MONTH:2, MM); STR(DAY:2, DD); IF MONTH < 10 THEN MM[1] := '0'; IF DAY < 10 THEN DD[1] := '0';

NOW := DD + '.' + MM + '.' + GG; { Немецкий формат }

28

END; {Конец функции NOW} Begin

Writeln('Введите имя основного файла: '); Readln(St);

Assign (Fs,St); Reset(Fs); { Открытие основного файла } Seek(Fs, FILESIZE(FS)); {Установка указателя на конец файла} While TRUE {Просмотр основного файла}

Do begin

Writeln('Enter - ввод сведений о студенте, ESC - конец: '); CH := READKEY; {Ожидание нажатия клавиши} IF ORD(CH) = 0

THEN CH1:=READKEY;{КЛАВИШИ ВТОРОЙ ГРУППЫ} IF ORD(CH) = 27 THEN break;

WRITE('Номер зачетки => '); READLN(S.TAB); WRITE('Фамилия => '); READLN(S.FIO); WRITE('Дата рождения => '); READLN(S.Data); WRITE('Группа => '); READLN(S.Grup);

WRITE('Размер стипендии, руб. => '); READLN(S.STEPA); S.DATE := NOW;

WRITE(Fs, S) end; Close(Fs); END.

29

ОБЪЕДИНЕНИЕ РАЗНОРОДНЫХ ЭЛЕМЕНТОВ (ЗАПИСЬ)

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

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

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

Например, анкетные данные о студенте вуза могут быть представлены в виде информационной структуры

Такая структура называется двухуровневым деревом. В Паскале эта информация может храниться в одной переменной типа record (запись). Задать тип можно следующим образом:

type < имя _ типа >=record <имя_поля1>: тип; <имя_поля2>: тип;

………………….

<имя_поля K >: тип end ;

где record – служебное слово, а <имя_типа> и <имя_поля> - правильные идентификаторы языка Паскаль.

Описание анкеты студента в Паскале будет выглядеть так: Пример фрагмента программы описания записи Паскаля

Type anketa=record fio: string[45]; pol: char;

30

dat_r: string[8]; adres: string[50]; curs: 1..5; grupp: string[3]; end;

Такая запись Паскаля, так же как и соответствующее ей дерево, называется двухуровневой.

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

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

Пример фрагмента программы описания записи Паскаля

Type anketa1=record fio: string[45]; pol: char;

dat_r: record; god: integer; mes: string[10]; den: 1..31; end;

adres: record gorod: string[25]; ulica: string [20]; dom, kv: integer; end;

curs: 1..5; grupp: string[3]; end;

Поля

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