Описание файлов с данными
В данной работе предлагается использовать два типа файлов с исходными данными. Оба файла содержат одну и ту же информацию, но хранят ее в разной форме. Файл с именем Dan.dat представляет собой типизированный файл, каждая запись которого представляет структуру фиксированного типа и размера. Структура каждой записи представлена ниже:
1-ое поле - string[15]; { поле для Фамилии И.О.}
2-ое поле - integer; {поле балла за 1 семестр}
2-ое поле - integer; {поле балла за 2 семестр}
2-ое поле - real; {поле для среднего балла }
Файл с именем Dan.txt является последовательным символьным файлом, в котором записи имеют различную длину. Каждая запись содержит фамилию, инициалы (в форме символ–точка–символ–точка без пробелов), две целочисленные оценки и средний балл, в виде изображения вещественного числа с фиксированной точкой. Каждое данное отделяется от соседних пробелом. Пример записи файла Dan.txt приведен ниже:
Петров П.П. 5 3 4.0
Работа с типизированным файлом достаточно проста. Описывается структура записи, содержащая указанные выше поля, Описывается типизированный файл, с записями такого типа (например с именем F), заводится переменная того же типа (например, с именем STUD). После открытия файла для чтения, оператором Read(F,STUD) в структуру STUD считывается информация об очередном студенте.
Работа с текстовым файлом осложнена тем, что в одной записи файла содержится как строка с именем и инициалами студента, так и числовая информация. Кроме того, длина фамилий в разных записях различна (но не больше 15 символов с инициалами). Организовать процедуру чтения одной записи можно по-разному. Например, можно прочесть всю запись в рабочую строку, затем найти в ней, где начинается первое число (символ из диапазона '0'..'9'), все до этого символа скопировать в строку – фамилию. Из остатка взять один символ и превратить в число стандартной процедурой Val(…), пропустить символ (пробел) и также получить вторую целочисленную оценку. Наконец, еще раз пропустив пробел, вырезать изображение среднего балла и процедурой Val(…)получить вещественное значение среднего балла. Все вырезаемые данные присваивать полям структуры данных, описывающих информацию о студенте.
В приведенном разборе контрольного варианта рассмотрен другой алгоритм чтения. Его суть заключается в следующем. Подготавливается рабочая строка длиной не менее 15 символов, для чего она заполняется каким-нибудь символом, в количестве 15 штук. Таким образом, в нулевом байте этой строки будет число 15 – текущая длина строки. Затем из записи файла читается по одному символу и заносится на соответствующее место в рабочую строку. Цикл прекращается, когда будет прочитан второй пробел (отделяющий инициалы от первой оценки). Так как при чтении ведется подсчет количества символов, в конце цикла известна длина фамилии. Из рабочей строки вырезается подстрока найденной длинны, начиная от первого символа, и копируется стандартной строковой функцией Copy(…) в поле структуры для фамилии студента.
Остаток входной записи читается обычным оператором Readln в две целочисленные и одну вещественную переменные для оценок (в поля структуры). Отметим, что в качестве рабочей строки можно использовать само строчное поле структуры.
В этом случае операторы чтения одной записи можно записать, например, так:
i:=0; { счетчик символов }
P:=0; { счетчик пробелов }
Stud.Name:='---------------'; {поле фамилии заполняем строкой из прочерков на всю длину }
while P<2 do {пока не прочтем второй пробел }
begin
inc(i); { стандартная процедура увеличения на 1 }
read(F,Stud.Name[i]); {читаем из файла по символу в поле имени }
if Stud.Name[i]=' ' then P:=P+1; { подсчет пробелов}
end;
Stud.Name:=Copy(Stud.Name,1,i); { Из полной строки поля фамилии вырезаем подстроку по второй пробел (включительно) и заносим обратно в поле фамилии }
readln(F,Stud.Bal1,Stud.Bal2,Stud.SrBal); { остаток записи файла читаем в поля оценок }
Разбор контрольного варианта
Задание
Таблица 31. Данные к заданию 31 варианта
№ вар. | Требования к записям, выбираемым из файла и помещаемым в список | Тип списка | Файл данных |
В начало – только с пятерками, в конец – только с тройками | S0D | dan.txt |
На основе общего задания и данных таблицы вариантов конкретное задание формулируется следующим образом:
Заполнить нульсвязный список дек данными из файла DAN.TXT,занося в начало дека записи о студентах, у которых все оценки – 5 баллов, а в конец дека – сведения о студентах, имеющих все оценки – 3 балла. Добавить в начало дека запись, в которой вместо фамилии указан тип списка, а вместо первой оценки – число записей с информацией в деке. Вывести в выводной текстовый файл таблицу записей из дека. Созданный дек удалить из памяти.
Содержание программы
Задание включает в себя следующие действия, подлежащие программированию:
- Формирование пустого дека;
- Открытие входного и выходного файлов;
- Чтение данных из файла с занесением нужных записей в дек с подсчетом их количества;
- Добавление информационной записи в начало дека;
- Вывод в выходной файл информационной записи из дека с удалением этой записи;
- Вывод "шапки" таблицы в выходной файл;
- Удаление из дека записей с выводом информации в табличном виде в выходной файл;
- Закрытие выходного файла.
При составлении программы, формирование дека и его распечатку в выходной файл оформим в виде процедур. Кроме того, отдельными процедурами оформим процессы добавления записи в дек и удаления (выбора) записи из дека.
Учитывая, что файл текстовый, воспользуемся процедурой чтения строки файла с распределением информации по полям структуры, как это рассмотрено в пояснениях выше.
Алгоритм
Укрупненные шаги алгоритма основной программы приведены под заголовком "содержание программы" (при оформлении отчета, алгоритмы следует включить в блок-схему программы). Рассмотрим строение процедур.
Добавление записи в дек. Исходя из схемы дека, приведенной на рис. 20, будем считать его пустым, если в нем нет ни одной записи, то есть указатели начала и конца дека указывают на nil. Если запись добавляется в пустой дек, то меняются оба указателя, если в непустой – меняется только один указатель (начала или конца). Проверку, что дек пуст, можно выполнять по содержимому любого указателя (nil или нет). Добавление записей к обоим концам выполняется почти одинаково: создается динамическая переменная типа звена дека, заполняется ее информационная часть, обе ссылки на соседние звенья делаются равными nil. Затем, в зависимости от того, к какому концу добавляется запись, меняется первая ссылка в новой записи (прицепляясь к деку) и вторая ссылка в концевой записи дека (связываясь с новой записью) или наоборот – вторая ссылка новой записи и первая ссылка конца дека. Наконец меняется указатель конца дека к которому добавлена запись.
Удаление записи из дека. В задаче требуется выбирать данные из дека с одной стороны, но для универсальности, составим процедуру выбора и удаления с заказанного конца дека. Как и в предыдущей процедуре, будем использовать логический параметр, равный истине, при работе с началом и лжи, при работе с концом дека. Если начало и конец ссылаются на одну и ту же область памяти (равны между собой), то при удалении записи дек становится пустым. Если нет, процесс выполняется в обратном порядке, по сравнению с добавлением записи.
Заполнение дека из текстового файла. Представляет собой цикл (пока не достигнут конец файла) чтения очередной строки текстового файла с заполнением информационной структуры, которую либо добавляют к началу дека (обе оценки – 5), либо к концу (обе оценки – 3), либо просто пропускают. Внутри цикла используются процедуры ввода одной строки и добавления записи в дек. При заполнении дека ведется подсчет количества записей дека.
Распечатка дека в выходной текстовый файл. Так как количество записей дека известно, вывод выполняется в форме арифметического цикла. Тело цикла содержит обращение к процедуре выбора записи из дека (из начала) и форматного вывода данных в выводной файл в виде строки таблицы.
При составлении блок-схем алгоритмов в отчете по лабораторной работе можно пользоваться именами переменных, описания которых предварительно заданы в таблице идентификаторов основной программы и подпрограмм.
Примечание: формальные параметры, передаваемые по имени (которым предшествует ключевое слово var), являются, по сути, указателями на данные, поэтому занимают в памяти по 4 байта, независимо от типа данных, на которые указывают.
Таблица идентификаторов
Таблица 32. Идентификаторы программы 31 варианта
Имя | Тип | Р-р, байт | Назначение |
Основная программа | |||
Lab_9 | имя программы | - | Работа с динамическими списками |
data | описатель | - | Описатель типа для структуры данных |
.Name | строка | Поле структуры данных для фамилии | |
.Bal1 | целое | Поле структуры данных для первой оценки | |
.Bal2 | целое | Поле структуры данных для второй оценки | |
.SrBal | вещественное | Поле структуры данных для средней оценки. | |
Pd | описатель | - | Описатель типа для указателей на звено дека |
Dek | описатель | - | Описатель типа для структуры данных записи дека |
P1 | указатель | Ссылка на следующий элемент дека | |
P2 | указатель | Ссылка на предыдущий элемент дека | |
Student | структура | Структура – составное поле данных записи дека | |
Docum | структура | Данные одной записи | |
DN | указатель | Указатель первого конца дека (начала) | |
DK | указатель | Указатель второго конца дека (конца) | |
Fin | последовательн. символьн. файл | Входной текстовый файл с данными | |
Fout | последовательн. символьн. файл | Выходной текстовый файл с таблицей результатов | |
k | целое | Количество элементов дека | |
GetStud – процедура выбора строки из входного файла | |||
St | указатель на структуру | Формальный параметр - адрес структуры с данными по студенту | |
F | указатель на файл | Формальный параметр - имя входного файла | |
P | целое | Счетчик пробелов во входной записи | |
i | целое | Порядковый № символа во входной записи | |
PutDek Процедура формирования нового звена дека | |||
NK | указатель | Формальный параметр - адрес конца дека, к которому добавляют запись | |
KN | указатель | Формальный параметр - адрес второго конца дека | |
Inf | структура | Формальный параметр – добавляемые данные | |
Beg | логическое | Признак, что добавляется к началу | |
U | указатель | Вспомогательный указатель на добавляемое звено дека | |
DelDek Процедура удаления крайнего звена дека | |||
NK | указатель | Формальный параметр - адрес конца дека, у которого удаляют запись | |
KN | указатель | Формальный параметр - адрес второго конца дека | |
Inf | указатель на структуру | Формальный параметр – адрес структуры, куда поместить выбираемые данные | |
Beg | логическое | Признак, что удаляется из начала | |
U | указатель | Вспомогательный указатель на удаляемое звено дека | |
ReadFile Процедура чтения записи из файла и заполнения дека | |||
F | указатель на файл | Формальный параметр – указатель на входной файл для входных данных | |
DekN | указатель | Формальный параметр - адрес указателя на начало дека | |
DekK | указатель | Формальный параметр - адрес указателя на конец дека | |
N | указатель | Формальный параметр - адрес переменной для числа элементов дека | |
Stud | структура | Рабочая структура для данных о студенте |
Разработанный алгоритм с использованием перечисленных идентификаторов реализуется на языке Турбо-Паскаль приведенной ниже программой.
Текст программы
Program Lab_9;
{ Программа Лабораторной работы N 9
Динамические переменные. Списки.
Вариант N 31.
А.Я.Умненькая, ст. гр. Я-007}
TYPE data = record {описатель структуры данных студента}
Name : string[15]; { поле для Фамилии И.О.}
Bal1,Bal2 : integer; {поля баллов за 2 семестра}
SrBal : real; {поле для среднего балла }
end;
Pd=^Dek; {описатель указателей на вершины дека}
Dek= record { описатель звена дека}
P1:Pd; {поле указателя следующего звена от начала}
P2:Pd; {поле указателя следующего звена от конца }
Student: data; { поле данных студента}
end;
VAR
Docum: data; {рабочая структура данных о студенте }
DN,DK : Pd; { указатели на начало и конец дека }
Fin,Fout:text; {Файлы входных данных и результатов работы}
k:integer; { количество элементов дека}
Procedure GetStud(Var F:text; var St:data);
{процедура чтения одной записи файла и формирования данных студента}
Var
P,i:integer;
Begin
i:=0;
P:=0;
St.Name:='---------------'; поле фамилии заполняем строкой из прочерков максимальной длины
while P<2 do {пока не прочтем второй пробел }
begin
inc(i);
read(F,St.Name[i]); {читаем из файла по символу в поле имени }
if St.Name[i]=' ' then P:=P+1; { подсчет пробелов}
end;
St.Name:=Copy(St.Name,1,i); { Из полной строки поля фамилии вырезаем подстроку по второй пробел (включительно) и заносим обратно в поле фамилии }
readln(F,St.Bal1,St.Bal2,St.SrBal); { остаток записи файла читаем в поля оценок }
End;
Procedure PutDek(Var NK,KN:Pd; Inf:data; Beg:boolean);
{процедура добавления элемента в дек с заказанного конца}
Var U:Pd;
Begin
New(U);
U^.Student:=Inf;
U^.P1:=nil;
U^.P2:=nil;
if NK=nil then { если дек перед этим был пуст }
KN:=U
else
if Beg then { если добавляем в начало }
begin
U^.P1:=NK;
NK^.P2:=U;
end
else { если добавляем в конец }
begin
U^.P2:=NK;
NK^.P1:=U;
end;
NK:=U;
End;
Procedure DelDek(Var NK,KN:Pd; var Inf:data; Beg:boolean);
{процедура выбора элемента из заказанного конца дека }
Var U:Pd;
Begin
U:=NK;
Inf:=U^.Student;
if NK=KN then { если в деке был всего один элемент}
begin { делаем дек пустым}
KN:=nil;
NK:=nil;
end
else
if Beg then { если удаление из начала }
begin
NK:=U^.P1;
NK^.P2:=nil;
end
else { если удаление из конца }
begin
NK:=U^.P2;
NK^.P1:=nil;
end;
Dispose(U); { собственно освобождение памяти от элемента }
End;
Procedure ReadFile(Var F:text; Var DekN,DekK:Pd; var N:integer);
{ чтение файла с заполнением дека }
Var
Stud:data;
Begin
N:=0;
While Not Eof(F) do
begin
GetStud(F,Stud);
N:=N+1;
if (Stud.Bal1=5) and (Stud.Bal2=5) then
PutDek(DekN,DekK,Stud,TRUE)
else
if (Stud.Bal1=3) and (Stud.Bal2=3) then
PutDek(DekK,DekN,Stud,FALSE)
else { если данные не заносим в дек }
N:=N-1;
end;
End;
Procedure WriteFile(Var F:text; Var NK,KN:Pd; N:integer);
{ процедура распечатки дека в выводной файл с удалением дека}
Var
i:integer;
Stud:data;
Begin
Writeln(F,'|--------------------------------------',
'--------------|');
Writeln(F,'| N | Фамилия И.О. | 1-й балл | 2-й ',
'балл | Ср.балл |');
For i:=1 to N do
begin
Writeln(F,'|---|----------------|----------|----',
'------|---------|');
DelDek(NK,KN,Stud,TRUE);
WriteLn(F,'|',i:2,' | ',Stud.Name:15,'|',Stud.Bal1:6,
' |',Stud.Bal2:6,' |', Stud.SrBal:6:1,' |');
end;
Writeln(F,'|--------------------------------------',
'--------------|');
End;
BEGIN { ОСНОВНАЯ ПРОГРАММА }
Assign(Fin,'Dan.txt');
Reset(Fin);
Assign(Fout,'Umnik9.res');
ReWrite(Fout);
DN:=nil;
DK:=nil;
k:=0;
ReadFile(Fin,DN,DK,k);
Close(Fin);
With Docum do
begin
Bal1:=k;
Bal2:=0;
SrBal:=0.0;
Name:='Список типа S0D';
end;
PutDek(DN,DK,Docum,TRUE); { добавление в начало дека записи со сводной информацией. }
DelDek(DN,DK,Docum,TRUE); {Выбор из дека сводной информации для печати }
Writeln(Fout, Docum.Name:20,' из ',Docum.Bal1,' строк');
WriteFile(Fout,DN,DK,k); {печать дека в файл с удалением элементов дека}
close(Fout);
End.
Варианты заданий
Таблица 33. Варианты заданий лабораторной работы № 9
№ вар. | Требования к записям, выбираемым из файла и помещаемым в список | Тип Списка | Файл данных |
Средний балл выше 3 | S2KI | dan.dat | |
Отличные оценки во всех полях | S2KO | dan.txt | |
Первые буквы фамилий А, Б, В | S0S | dan.dat | |
Оценки во всех полях ниже 4 баллов | S0O | dan.txt | |
Первая оценка выше 3 | S1L | dan.dat | |
Первая или вторая оценка выше 3 | S1KI | dan.txt | |
Первая оценкой ниже 5, а вторая выше 3 | S1KO | dan.dat | |
Первые буквы фамилий А, В, Е и средние оценки выше 3 | S2L | dan.txt | |
Оценки 3 во всех полях оценок | S2KI | dan.dat | |
Средние оценки выше 3 баллов | S2KO | dan.txt | |
Вторая оценка выше 4, а первая выше 3 баллов | S0S | dan.dat | |
Первая и вторая оценки выше 4 баллов | S0O | dan.txt | |
В начало –со средней оценкой <4 балла, в конец – прочие | S0D | dan.dat | |
Первые буквы фамилий А, Б, В и средние оценки выше 4 | S1L | dan.txt | |
Вторые и средние оценки выше 4 баллов | S1KI | dan.dat | |
Первая оценка выше 3 баллов, а средняя оценка выше 3,5 | S1KO | dan.txt | |
Все оценки не ниже 4 баллов | S2L | dan.dat | |
Вторая и средняя оценки выше 4 баллов | S2KI | dan.txt | |
Средняя оценка выше 3,5 баллов | S2KO | dan.dat | |
Средняя оценка ниже 4,5 и фамилии начинаются с букв А и В | S0S | dan.txt | |
Средняя оценка выше 4 баллов и фамилии, начинаются с букв от А до К | S0O | dan.dat | |
В начало – если первая оценка 3, в конец – если первая – 5 | S0D | dan.txt | |
Первая оценка выше 3 балов, а средняя оценка 4 балла | S1L | dan.dat | |
Все оценки выше 4 и фамилии начинаются с букв А - Е | S1KI | dan.txt | |
Первая и средняя оценки выше 4 баллов | S1KO | dan.dat | |
Все оценки ниже 5 баллов | S2L | dan.txt | |
Первая или вторая оценка выше 4 баллов | S2KI | dan.dat | |
Первая или средняя оценки ниже 4 баллов | S2KO | dan.txt | |
Средняя оценка выше 4 баллов | S0S | dan.dat | |
Все оценки выше 4 или фамилии начинаются с букв А - Е | S0O | dan.txt | |
В начало – только с пятерками, в конец – только с тройками | S0D | dan.txt |
Литература
- Бородич Ю.С., Вальвачев А.Н., Кузьмич А.И. Паскаль для персональных компьютеров: Справочне пособие. Минск: "Вышейшая школа", 1991.
- Епанешников А.М., Епанешников В.А. Программирование в среде Turbo Pascal 7.0 (третье издание). М.: "Диалог-МИФИ", 1996.
- Немнюгин С.А. Turbo Pascal: практикум. СПб.: Питер, 2001.
- Кнут Д. Искусство программирования для ЭВМ. /Пер. с англ.: В 3-х томах. т.3. Сортировки и поиск. М.: Мир, 1976.
- Бронштейн И.Н., Семендяев К.А. Справочник по математике для инженеров и учащихся ВТУЗов (Изд. 13-е). М.: "Наука", 1986.
Приложение А.
Система меню и команды Турбо-Паскаля