Информатика_ч2-записи
.pdf21
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;
Поля
После того, как определен тип записи Паскаля, можно определять переменную этого типа. Переменная определяется путем задания ее идентификатора и указания типа.