Выводы по проделанной работе. 5 страница
TFIOElem = string[40]; {Элемент массива с Ф.И.О.}
{Тип - массив из 10 строк, каждая длиной до 40 символов: }
TFIOArray = array[1..10] of TFIOElem;
{Сортировка строковых элементов массива самым простым способом}
{Используется алгоритм "Сортировка выбором"}
procedure DoSortArray(FioCount: Integer; var FioArray: TFIOArray);
Var
I, J: Integer;
STmp: TFIOElem;
MinStrIdx: Integer;
Begin
for I := 1 to FioCount - 1 do {Внешний цикл перебора слева-направо}
Begin
MinStrIdx := I; {Пока считаем i-ый элемент минимальным}
{Отыскиваем строку, которая должна располагаться раньше по алфавиту}
for J := I + 1 to FioCount do {Внутренний цикл перебора}
if FioArray[J] < FioArray[MinStrIdx] then
MinStrIdx := J; {Запоминаем индекс найденной строки}
{Если такая строка найдена, то меняем ее с i-ым элементом}
if MinStrIdx <> I then
Begin
STmp := FioArray[I]; {Запоминаем i-ый элемент}
{Записываем в него новое значение}
FioArray[I] := FioArray[MinStrIdx];
FioArray[MinStrIdx] := STmp;
end;
end;
end; {END of DoSortArray}
{Процедура MakeSort осуществляет сортировку строки с Ф.И.О.,
заданной с помощью аргумента St }
procedure MakeSort(var St: string);
Var
{Объявляем массив из 10 строк, каждая длиной до 40 символов}
StrAr: TFIOArray;
I, Len, CurArIdx: Integer;
Begin
{Заменяем все последовательности ", " на ","}
while Pos(', ', St) > 0 do
Delete(St, Pos(', ', St) + 1, 1);
Len := Length(St); {Запоминаем длину строки}
I := 1; {Инициализируем счетчик цикла WHILE}
CurArIdx := 1; {Инициализируем счетчик текущего элемента массива}
StrAr[CurArIdx] := ''; {Предварительная очистка строки}
{В цикле осуществляем разбивку исходной строки. В результате этого
каждое Ф.И.О. будет храниться в отдельном элементе массива StrAr,
а в переменную CurArIdx будет записано общее количество Ф.И.О.}
while I <= Len do {Пока не достигнут конец строки}
Begin
if St[I] = ',' then {Если встретили запятую}
Begin
Inc(CurArIdx); {Делаем текущим следующий элемент массива}
StrAr[CurArIdx] := ''; {Предварительная очистка строки}
End
else {Добавляем символ к текущему строковому элементу массива}
StrAr[CurArIdx] := StrAr[CurArIdx] + St[I];
Inc(I); {Увеличиваем счетчик цикла на единицу}
end; {Конец цикла WHILE}
{Выводим на экран промежуточные результаты работы программы
- список всех найденных Ф.И.О., каждое на отдельной строке}
for I := 1 to CurArIdx do
Writeln('FIO ', I, ': ', StrAr[I]);
{Сортировка элементов строкового массива}
DoSortArray(CurArIdx, StrAr);
St := ''; {Очищаем строку St}
{Записываем в var-параметр St отсортированный список Ф.И.О.}
for I := 1 to CurArIdx do
Begin
St := St + StrAr[I]; {Выполняем "сцепление" строк}
if I < CurArIdx then {Если Ф.И.О. не последнее}
St := St + ', '; {то добавляем после него ", "}
end;
end; {END of MakeSort}
Var
S: string;
begin {Начало основной программы}
Writeln('Введите несколько Ф.И.О. через запятую:');
Readln(S); {Ввод строки}
MakeSort(S); {Обработка строки (сортировка Ф.И.О.)}
Writeln('Список Ф.И.О. после сортировки:');
Writeln(S); {Вывод результатов на экран}
Readln;
end.
6.7. Варианты заданий
Количество слов в строке и максимальный размер каждого их слов выбираются студентом. Рекомендуется работать с латинскими символами.
1) Поменять местами слова с максимальной и минимальной длиной при выполнения условия, что такие слова единственные
2) Заменить окончания (последние два символа) на 'xz' в словах, длина которых равна 5
3) Поменять местами слово, начинающееся на 'a', со словом, оканчивающимся на 'z', при условии, что такие слова существуют и являются единственными
4) Удалить последние 3 символа из слов, начинающихся на 'a'
5) Удалить первые 3 символа из слов, оканчивающихся на 'th'
6) Дополнить символом '*' слова, имеющие длину меньше заданной (максимальной) до максимальной
7) Заменить первые 3 символа слов, имеющих выбранную длину, на символ '*'
8) Удалить все символы 'а' из слов, длина которых равна выбранной
9) Заменить все символы 'a' на 'd' в словах, длина которых меньше выбранной
10) Заменить первые строчные буквы на заглавные в каждом слове, длина которого больше выбранной
11) Вставить пробел после первых 2-х символов в слова, имеющие длину, на 1 меньше заданной
12) Заменить первую строчную букву на заглавную в словах, имеющих выбранную длину
13) Вставить пробел перед последними 2-мя символами в слова, имеющие минимальную (заданную) длину
14) Посчитать количество гласных букв в строке и заменить их на '*'
15) Упорядочить символы в строке по алфавиту
16) Вывести все слова, у которых первая и последняя буквы одинаковые
17) Упорядочить строку по убыванию длин слов
18) Проверить в математическом выражении, заданном строкой, соответствие открывающих и закрывающих скобок
19) Вывести все слова в строке в обратном порядке
20) Удалить двойные пробелы и переместить все найденные в тексте цифры в конец строки
6.8. Содержание отчета (см. п. 1.10)
6.9. Контрольные вопросы
1) Что такое строка?
2) Каким идентификатором определяются данные строкового типа?
3) Какова максимальная длина строки? Как определить длину строки?
4) Какие выражения называются строковыми?
5) Какие операции допустимы над строковыми данными?
6) Каким образом производится сравнение строк?
7) Как можно обратиться к отдельным символам строки?
8) Каково назначение специальных процедур и функций обработки данных строкового типа?
Лабораторная работа № 7. Работа с файлами на языке Pascal
7.1. Цель работы
Приобретение навыков разработки Pascal-программы для работы с файлами с целью длительного хранения и загрузки необходимой информации.
7.2. Задание на лабораторную работу
Составить программу на языке Pascal, осуществляющую ведение информационного справочника в соответствии с вариантом задания (п. 7.7).
7.3. Требования к программе
Программа должна выводить:
– номер варианта, назначение программы и ФИО автора;
– информационные сообщения о необходимости ввода данных;
– результаты работы в соответствии с вариантом задания.
Программа должна обеспечивать следующие операции (п. 7.6):
1) загрузка справочника из файла;
2) добавление новой записи;
3) вывод на экран списка всех записей;
4) поиск записи по заданному атрибуту;
5) сохранение справочника в файл.
7.4. Порядок выполнения работы
1) Получить вариант задания (п. 7.7).
2) Изучить операторы объявления данных типа «запись» и операторы обработки типизированных и текстовых файлов (п. 7.5).
3) Разработать и отладить программу, осуществляющую ведение информационного справочника.
4) Ответить на контрольные вопросы (п. 7.9).
5) Оформить отчет (см. п. 1.11).
7.5 Основы работы с записями и файлами на языке Pascal
7.5.1. Определение типа данных RECORD
Помимо простых типов данных (Real, Integer, Byte, Char, Boolean и т.п.), а также массивов (array) и строк (string), в языке Pascal присутствует структурированный тип данных «запись», объявляемый с помощью ключевого слова RECORD. В ряде других языков программирования аналогичная языковая конструкция называется «структурой» (STRUCT). Формат объявления типа «запись»:
Type
<наименование_типа> = record
<поле_1>: <тип_поля>;
<поле_2>: <тип_поля>;
<поле_N>: <тип_поля>;
end;
Таким образом, запись состоит из произвольного количества полей, каждое их которых может иметь любой известный компилятору тип (в том числе RECORD). Пример объявления типа RECORD и переменных:
type{Объявление типа}
TBirthDay = record
Fam: string[40]; {фамилия}
Day, Month: Byte; {день и месяц рождения}
Year: Word; {год рождения}
end;
var{Объявление переменых}
a, b: TBirthDay;
В этом примере тип TBirthDay (день рождения) представляет собой запись с полями Day, Month и Year (день, месяц и год), а также Fam (фамилия). Переменные a, b являются записями типа TBirthDay. Таким образом, в одной переменной объединено (инкапсулировано) сразу несколько разнотипных данных.
Между однотипными переменными-записями допускается операция присваивания, например: a := b.
Для доступа к полю записи (с целью его чтения или модификации) необходимо использовать составное имя, в котором имя поля отделяется от имени переменной символом «.» (точка), например:
a.Day := 1;
a.Month := 12;
a.Year := 2011;
a.Fam := 'Ivanov';
Следующий пример демонстрирует объявление массива, состоящего из элементов-записей, а также вывод содержимого массива на экран:
Var
Mas: array[1..10] of TBirthDay;
....................
for I := 1 to 10 do
Writeln('Запись №', I, ': Фамилия=', Mas[I].Fam,
', Дата рожд=', Mas[I].Day, '.', Mas[I].Month, '.', Mas[I].Year);
Как видим, элементы массива, помимо простых типов и строк, могут быть также структурированными.
Приведенный пример является простым и очевидным, однако этого удалось достичь лишь за счет использования структурированных типов. Объединение (инкапсуляция) некоторого набора разнотипных данных в одной структуре – очень распространенный приём в программировании.
7.5.2. Операторы для работы с файлами в Pascal
В независимости от используемого языка программирования (Pascal, C++, Java и т.д.), операционной системы (Windows, Linux, Unix, Mac-OS и т.д.), аппаратной архитектуры (персональный компьютер, сотовый телефон, промышленный контроллер и т.д.), рано или поздно перед программистом возникнет задача длительного хранения данных, необходимых для работы программы. В качестве энергонезависимого носителя, на который может быть сохранена информация для длительного хранения, могут выступать: дискета, кассетная лента, flash, fram, жесткий диск (магнитный или полупроводниковый) и т.д. В зависимости от используемой операционной системы, различаются способы организации информации на тех или иных носителях. Крайний и наиболее сложный случай заключается в том, что программист должен полностью продумывать механизмы хранения информации, вплоть до оперирования с памятью на уровне отдельных ячеек или кластеров. При использовании современных операционных систем (Windows, Linux и т.д.) вся сложность работы с тем или иным устройством прячется от программиста. В замен ему предлагается работать с более понятным и универсальным объектом – «файл». Файл – это именованный участок данных, сохраненных в энергонезависимой памяти (например, на флешке). Совершенно не важно, как физически хранятся данные (везде – по-разному). Программиста это вопрос не должен интересовать. Операционная система предоставляет программисту готовые функции для работы с файлами, осуществляющие: создание, открытие, закрытие файла, запись в файл, чтение из файла и ряд других функций.
После изучения (в достаточной мере) принципов работы с файлами, реализация (в будущей работе) иных способов хранения информации не должна представлять чрезмерных трудностей.
Хотя информация в файле всегда хранится в виде некоторого набора байтов, принято различать текстовые файлы (их можно открыть в простейшем текстовом редакторе, например «Блокнот», и внести необходимые изменения) и двоичные файлы (попытка открытия таких файлов в блокноте ничего полезного не даст). Как бы то ни было, перед началом работы с файлом необходимо объявить и инициализировать некоторую переменную (в терминологии языка Pascal – файловую переменную), после чего ее необходимо указывать в каждом операторе, выполняющем ту или иную работу с файлом.
Наиболее простой и понятной является работа с текстовыми файлами, поскольку пользователь в любой момент может открыть созданный файл из программы «Блокнот» и, при необходимости, внести в него свои изменения. В приведенном ниже примере программа в цикле требует от пользователя ввести строку, пока он не введет символ «*». Все вводимые строки будут сохраняться в указанный текстовый файл.
Var
F: Text; {Объявление файловой переменной}
S: string[100];
Begin
Assign(F, 'C:\TEMP\MyFile.txt'); {Инициализируем файловую переменную}
Rewrite(F); {Создаем текстовый файл с правом на запись}
repeat{основные действия выполняются в цикле}
Write('vvedite stroku> '); {вывод приглашения на экран}
Readln(S); {ожидаем, пока пользователь не введет строку}
Writeln(F, S); {запись введенной строки в файл}
until S = '*'; {если ввели "*", то выход из цикла}
Close(F); {Закрываем файл}
end.
После окончания работы с программой предлагается открыть полученный файл с помощью блокнота и убедиться в наличии всех введенных строк.
При работе с файлом необходимо в первую очередь инициализировать файловую переменную (в данном случае F: Text). Файловая переменная внутренне объявлена как RECORD с многочисленными полями, в том числе «имя файла» и «файловый указатель». Таким образом, данная структура является самодостаточной, что избавляет от необходимости многократного указания имени файла. Далее файл необходимо открыть. Существует несколько процедур открытия файлов (это связано с существованием большого количества режимов открытия), в результате успешной работы которых происходит открытие файла, после чего можно с ним работать, т.е. выполнять операции ввода/вывода. После завершения всех операций ввода/вывода файл необходимо закрыть. Если этого не сделать, то дальнейшие попытки открыть файл (например, в другой процедуре) могут окончиться неудачно.
Помимо текстовых файлов, язык Pascal существенно упрощает работу также и с двоичными файлами. Двоичный файл в общем случае нельзя открыть с помощью блокнота, однако, это более предпочтительный, универсальный и экономичный формат для хранения разного рода информации. Проще всего работать с «типизированными» двоичными файлами, поскольку в этом случае работа с файлом напоминает работу с одномерным массивом. Например, так выглядит код сохранения элементов массива в типизированный файл:
Var
F: file of TBirthDay; {Объявляем файловую переменную}
I: Integer;
Begin
Assign(F, 'MyFile.bin');{Инициализируем файловую переменную}
Rewrite(F); {Открываем файл для записи}
for I := 1 to ACount do {Цикл перебора записей}
Write(F, Mas[I]); {Сохраняем запись в файл}
Close(F); {Закрываем файл}
end;
Кроме того, Pascal позволяет работать с файлами, которые невозможно отнести ни к одному известному типу. В терминологии языка Pascal такие файлы являются «нетипизированными» (фактически – это массив байтов). На самом деле это наиболее привычный для большинства программистов способ представления, поскольку в таких файлах можно хранить совершенно любую информацию, как текстовую, так и двоичную. Разумеется, организация работы с нетипизированными файлами является несколько более сложной. Для ввода/вывода вместо процедур Read и Write используются процедуры BlockRead и BlockWrite. Более подробную информацию по работе с нетипизированными файлами в языке Pascal читатель может с легкостью получить в других источниках.
Процедуры по обработке текстовых и типизированных файлов, требуемые для выполнения данной лабораторной работы, приведены в таблице 7.1.
Таблица 7.1 – Процедуры для работы с файлами
Процедура | Назначение |
Assign() | Связывает имя файла с файловой переменной |
Append() | Открывает текстовый файл для добавления новых записей в конец файла. Если файл не найден, то возникнет ошибка |
Reset() | Открывает текстовый файл для чтения, или двоичный файл для чтения/записи. Если файл не найден, то возникнет ошибка |
Rewrite() | Открывает текстовый или двоичный файл для записи. Файл будет создан и обнулен автоматически |
Write() | Записывает заданное значение в файл |
Writeln() | Записывает строку текста и символы перевода строки в текстовый файл |
Read() | Считывает информацию из файла в заданную переменную |
Readln() | Считывает строку из текстового файла |
Eof() | Проверяет, не достигнут ли конец файла при чтении |
Erase() | Уничтожает заданный файл |
Close() | Выполняет закрытие файла |
7.6 Пример программы
В приведенном ниже примере реализована программа «Телефонный справочник», отвечающая требованиям к данной лабораторной работе (п. 7.3). Программа подробно прокомментирована, поэтому не нуждается в дополнительных пояснениях. Рекомендуется тщательным образом проанализировать предлагаемый пример, поскольку в нем найдутся ответы на многие вопросы, которые, несомненно, возникнут при выполнении работы.
program PhoneBook; {Программа "Телефонная книга"}
Const
AFileName = 'phones.spr'; {Имя файла}
MaxRecordCount = 100; {Максимальное число записей в справочнике}
Type
TFam = string[20]; {Объявление типа "фамилия абонента"}
{Описание записи из телефонной книги}
TPhoneRec = record
Fam: TFam; {фамилия абонента}
Phone: string[15]; {телефон абонента}
Pol: Boolean; {пол абонента TRUE-муж, FALSE-жен}
BirthYear: Word; {год рождения}
end;
{Объявление типа-массива записей телефонной книги}
TPhoneTable = array[1..MaxRecordCount] of TPhoneRec;
{Процедура выполняет загрузку справочника из файла}
procedure LoadFromFile(var ATable: TPhoneTable; var ACount: Integer);
Var
F: file of TPhoneRec; {Объявляем файловую переменную}
Begin
Assign(F, AFileName); {Инициализируем файловую переменную}
{$I-} {Отключаем генерацию ошибок ввода/вывода}
Reset(F); {Открываем файл для чтения}
{$I+} {Возвращаем директиву "I" в исходное состояние}
if IOResult = 0 then {Если файл успешно открыт}
begin {то считываем из него записи}
ACount := 0; {Обнуляем счетчик записей}
while not Eof(F) do {Пока не достигнут конец файла...}
Begin
Inc(ACount); {Увеличиваем ACount на единицу }
Read(F, ATable[ACount]); {Считываем запись из файла}
end;
Close(F); {Закрываем файл}
Writeln('Spravochnik uspeshno zagruzhen iz faila: ', AFileName,
'. Kolichetvo zapisei: ', ACount);
end else {Иначе выводим сообщение "Файл не найден!"}
Writeln('ERROR: Fail ne naiden!');
end;
{Процедура выполняет сохранение справочника в файл}
procedure SaveToFile(const ATable: TPhoneTable; const ACount: Integer);
Var
F: file of TPhoneRec; {Объявляем файловую переменую}
I: Integer;
Begin
Assign(F, AFileName); {Инициализируем файловую переменную}
Rewrite(F); {Открываем файл для записи}
for I := 1 to ACount do {Цикл перебора записей}
Write(F, ATable[I]); {Сохраняем запись в файл}
Close(F); {Закрываем файл}
Writeln('Spravochnik uspeshno sohranen v fail: ', AFileName);
end;
{Процедура выводит на экран запись с указанным номером}
procedure ShowRecord(const ATable: TPhoneTable; Num: Integer);
Var
ARec: TPhoneRec;
C: Char;
Begin
ARec := ATable[Num];
if ARec.Pol then {if ARec.Pol = True}
C := 'm'
Else
C := 'f';
Writeln('No', Num, ': Fam=', ARec.Fam, ', Tel=',
ARec.Phone, ', Pol=', C, ', God rozhd=', ARec.BirthYear);
end;
{Процедура выводит на экран список всех записей}
procedure ShowAllRecords(const ATable: TPhoneTable;
const ACount: Integer);
Var
I: Integer;
Begin
Writeln('Spisok vseh zapisei:');
for I := 1 to ACount do
ShowRecord(ATable, I);
end;
{Процедура добавления новой записи в справочник}
procedure AddNewRecord(var ATable: TPhoneTable; var ACount: Integer);
Var
ARec: TPhoneRec;
C: Char;
Begin
Write('Vvedite familiu: ');
Readln(ARec.Fam);
Write('Vvedite telefon: ');
Readln(ARec.Phone);
Write('Vvedite pol ("m" / "f"): ');
Readln(C);
if C = 'm' then
ARec.Pol := True {Мужчина}
else { if C = 'f' }
ARec.Pol := False; {Женщина}
Write('Vvedite god rozhdenia: ');
Readln(ARec.BirthYear);
Inc(ACount); {Увеличиваем счетчик записей на 1}
ATable[ACount] := ARec; {Записываем запись ARec в массив ATable}
Write('Dobablena zapis: ');
ShowRecord(ATable, ACount); {Печатаем запись на экран}
end;
{Процедура отыскивает запись по заданной фамилии и печатает на экране}
procedure FindRecord(const ATable: TPhoneTable; const ACount: Integer);
Var
I: Integer;
IsFind: Boolean;
Fam: TFam;
Begin
Write('Vvedite familiu: ');
Readln(Fam);
IsFind := False; {Сбрасываем флаг перед поиском}
for I := 1 to ACount do
if ATable[I].Fam = Fam then
Begin
IsFind := True; {Устанавливаем флаг "Успешный поиск"}
Write('Zapis naidena: ');
ShowRecord(ATable, I);
Break; {Выход из цикла}
end;
if not IsFind then {Если запись не найдена}
Writeln('Zapis ne naidena!');
end;
Var
MenuNum: Byte;
PhoneTable: TPhoneTable; {Переменная-справочник}
PhoneCount: Integer; {Текущее количество записей в справочнике}
Begin
PhoneCount := 0; {При запуске программы справочник еще пуст}
Writeln('Telefonnaya kniga. Avtor: Ivanov I.I');
Repeat
Writeln('Vvedite cifru dlya vypolneniya deistviya:');
Writeln('1 - zagruzka spravochnika iz faila');
Writeln('2 - novaya zapis');
Writeln('3 - spisok vseh zapisei');
Writeln('4 - poisk zapisi po familii');
Writeln('5 - sohranenie spravochnika v fail');
Writeln('6 - vyhod iz programmy');
Write('> ');
Readln(MenuNum);
case MenuNum of
1: LoadFromFile(PhoneTable, PhoneCount);
2: AddNewRecord(PhoneTable, PhoneCount);
3: ShowAllRecords(PhoneTable, PhoneCount);
4: FindRecord(PhoneTable, PhoneCount);
5: SaveToFile(PhoneTable, PhoneCount);
end;
until MenuNum = 6;
end.
Следует отметить, что благодаря разбивке программного кода на подпрограммы, основная часть программы оказалась очень простой. Несмотря на «солидный» размер (более 150 строк кода), разобраться в данном примере не составит чрезмерных трудностей (при условии последовательного выполнения всех предыдущих лабораторных работ).
7.7. Варианты заданий
№ | Наименование справочника, поля | Тип файла | Поле поиска |
Контрагенты. Поля: наименование контрагента, ИНН, тип (True-юридическое, False-физическое лицо), год регистрации | Текстовый | Наименование контрагента | |
Владельцы сотовых телефонов. Поля: ФИО владельца, модель телефона, идентификатор телефона (IMEI), дата приобретения | Типизированный | ФИО владельца | |
Участники интернет-форума. Поля: ФИО участника, пол (True-мужской, False-женский), пароль, дата регистрации, | Текстовый | ФИО участника | |
Улицы вашего города. Поля: название улицы, количество домов, протяженность (км), год основания | Типизированный | Название улицы | |
Товарно-материальные ценности. Поля: наименование ТМЦ, штрих-код, количество на складе, стоимость | Текстовый | Наименование ТМЦ | |
ГородаУкраины. Поля: наименование города, год основания, число жителей, площадь | Типизированный | Наименование города | |
Учебная нагрузка группы. Поля: наименование предмета, количество часов, ФИО преподавателя, система оценки знаний (True-экзамен, False-зачет) | Текстовый | Наименование предмета | |
Расписание занятий группы. Поля: день недели, тип недели (True-первая, False-вторая), наименование предмета, время начала | Типизированный | Наименование предмета | |
Расписание назначенных встреч. Поля: ФИО, место встречи, дата и время встречи | Текстовый | ФИО | |
Сотрудники. Поля: ФИО, должность, дата приема на работу, оклад | Типизированный | ФИО | |
Поступления ТМЦ.Поля: наименование ТМЦ, наименование поставщика, количество, дата оприходования | Текстовый | Наименование ТМЦ |