Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторная работа 6.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
140.29 Кб
Скачать

Лабораторная работа № 6 обработка файлов средствами языка турбо паскаль

6.1 Цель работы

Освоение методики проектирования Паскаль-программ с использованием динамических переменных и операций с файлами.

6.2 Задание на лабораторную работу

6.2.1 Получить вариант задания (п. 6.4).

6.2.2 Изучить методику работы с динамическими переменными.

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

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

6.2.5 Программа должна выполнять вывод сообщения о назначении программы, ввод данных в режиме диалога.

6.2.6 Программа должна ввести в память динамически объявленный массив (двумерный) и записать его в файл при помощи нетипизированной файловой переменной и выполнить действия над данными таблицы 15.

6.2.7 Подготовить текст программы и отладить программу с использованием среды Турбо-Паскаля. Выполнять контроль ошибок при открытии файлов.

6.2.8 Ответить на контрольные вопросы (п.6.6).

6.2.9 Оформить отчет (п. 6.5).

6.3 Справки по файловым процедурам и функциям

6.3.1 Динамические переменные.

Содержательно любой ссылочный тип определяет множество значений, которые являются указателями на значения некоторого определенного типа. Для описания ссылочных типов используется символ “^” и идентификатор типа, например:

type

P=^integer;

Ссылочные типы в описаниях переменных можно задавать как посредством идентификаторов, так и явно:

var

P1, P2 : P;

OneMan : ^Person;

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

i:=i+2; P1^:=P1^+2; полностью эквивалентны.

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

Основные действия над динамическими переменными — создание и уничтожение — реализуется в языке Паскаль стандартными процедурами New и Dispose.

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

В динамической области памяти отводится место для хранения переменной, тип которой совпадает с базовым типом указателя—параметра.

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

Var

P:^Person;

begin

New(P);

После этого доступ к созданной динамической переменной возможен (только) через указатель P:

P^.Name:=’Иван’;

P^.Sex:=Male;

Для освобождения памяти, отведенной с помощью процедуры New, используется “симметричная” ей стандартная процедура Dispose. Параметром этой процедуры должен быть указатель на динамическую переменную, причем эта переменная должна быть ранее размещена в куче посредством процедуры или функции New; при этом тип размещаемой переменной должен совпадать с базовым типом параметра процедуры Dispose.

Var

P:^Person;

Begin

New(P);

{Действия с указателем Р}

Dispose(P);

End.

Pascal содержит еще ряд системных программ, работающих с динамической памятью и указателями. Функция MaxAvial, которая возвращает максимальный размер непрерывного участка свободной памяти. Функция MemAvial — возвращает общий объем свободных областей. Для определения размера необходимой памяти можно воспользоваться стандартной функцией SizeOf.

if MaxAvial >= SizeOf(Person) then

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

Функции Seg и Ofs позволяют определить компоненты адреса любого значения. Эти функции имеют один параметр любого типа и возвращают, соответственно, значение сегмента и смещения для переменной, переданной в параметре. Возвращаемые значения имеют тип word.

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

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

{$M16384,0,655360},

то есть под стек выделяется 16К байт, под динамическую память — вся свободная оперативная память, доступная операционной системе.

Рассмотрим пример программы для размещения двумерного массива в динамической памяти:

type

PStr = ^Str;

Str = array [0..Max] of byte;

Col = array [0..Max] of PStr;

Var

i, j, M, N: integer;

Image: Col;

begin

For i:=0 to M-1 do begin

GetMem (Image[i], N);

For j:=0 to N-1 do Image[i]^[j]:=…

End;

End.

6.3.2 Описание файловых переменных

Файл - поименованная область памяти на внешнем запоминающем устройстве, предназначенная для хранения данных.

Файл можно представить как список значений одного и того же (базового) типа; количество значений в программе не ограничивается.

Все элементы файла считаются пронумерованными; начальный элемент имеет нулевой номер.

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

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

Переменные файлового типа определяются по следующему правилу:

<Имя переменной>: file оf <базовый тип>;

где «базовый тип» - любой допустимый в Паскале тип, кроме файлового типа.

Примеры описаний файловых типов и переменных:

Type

Person = record Name: string[40];

Tarif: Longint

end;

FP = flie of Person;

Var F1, F2 : FP; {Fl, F2 - файловые переменные для представления в программе файлов, которые могут содержать записи типа Person}

Symb_F : file of char; {переменная для работы с файлами, которые могут содержать символы}

Table : file of String[80]; {переменная для работы с файлами, которые могут содержать символы}

InputData : file of real;

F: file of integer;

6.3.3 Установочные и завершающие операции с файлами

В эту группу входят четыре операции, реализованные в виде стандартных процедур со следующими именами: Assign, Reset, Rewrite, Flush, Close.

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

Правило обращения к процедуре Assign:

Assign (F, S);

F - имя файловой переменной, S - строковое выражение, образующее имя файла по правилам, принятым в операционной системе MS-DOS (может включать в себя обозначение дисковода, цепочку каталогов и имя файла).

Пример: Assign (F. 'D:\MYDlR\MyFile.dta');

После выполнения данного вызова файловая переменная F будет связана с дисковым файлом MyFile.dta, расположенным в каталоге MYDIR из корневого каталога диска D).

S:='RESULT.DAT;

Assign (F1, S);

write('Bвeдитe имя файла для сохранения данных:');

readln(S); Assign (F2, S);

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

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

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

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

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

Процедура Flush(F) используется для завершения обменов с файлом без его закрытия. Обмены с файлами всегда реализуются через некоторый буфер в оперативной памяти, поэтому в процессе записи в файл последние записываемые элементы могут еще находиться («застрять») в буфере. Процедура Flush вызывает принудительный сброс этих элементов в файл.

Процедура Close(F) завершает действия с файлом. При этом ликвидируются внутренние буферы, образованные при открытии файла, оставшиеся в буфере данные сохраняются в файле.

6.3.4 Операции ввода-вывода.

Операции ввода-вывода (чтения из файла и записи в файл) реализуются с помощью процедур: Read, Write.

Правила записи операторов:

Read(F, А, В, С,...);

Write(F1,X,Y,Z,...);

F, F1 - имена файловых переменных, к которой были применены операции открытия Reset и Rewrite соответственно,

А, В, С - переменные, типы которых совпадают с базовым типом файловой переменной F,

X, Y, Z - выражения, типы которых совпадают с базовым типом файловой переменной F1. Примеры:

Read(F, А); {чтение значения одного элемента файла)

Read(F, В, С); {последовательное чтение значений двух элементов}

Read(F, M[i]); {чтение значения одного элемента и присвоение прочитанного значении элементу массива (запись в массив)}

Write(F1, X); {запись значения одной переменной в файл}

Write(F1, X, Y); {последовательная запись значений двух

переменных в файл}

Write(F1, M[i]); {запись значения i-го элемента массива в файл}

Write(F1, X, Х*Х, X+Y, X/(Y-Z)); {последовательная запись

значений указанных выражений в файл}

Выполнение процедуры Read происходит следующим образом. Начиная с текущей позиции указателя файла будут последовательно читаться значения, содержащиеся в файле. Каждое прочитанное значение будет присваиваться очередной переменной из тех, которые указаны в вызове процедуры. После чтения очередного значения указатель файла будет смещаться на следующую позицию. Если в процессе выполнения процедуры Read текущий указатель файла будет установлен на позицию, не содержащую информации (то есть будет достигнут конец файла), то чтение будет прекращено, процедура Read завершится преждевременно, и возникнет ситуация "конец файла". Возникновение этой ситуации можно проверить с помощью встроенной функции EOF.

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

6.3.5 Перемещения по файлу, определение параметров файла.

Процедура Seek(F, N) позволяет явно изменить значение текущего указателя, установив его на элемент файла с заданным номером (N), где F -переменная файлового типа, N - переменная типа Longint.

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

Функция FileSize(F) определяет число элементов файла.

Функция FilePos(F) определяет номер элемента, на который установлен текущий указатель.

Примеры использования:

Seek (F, FilePos (F)+1) { пропуск одного элемента }

Seek (F, FileSize (F)) {установка текущего указателя непосредственно

за последним элементом файла; это может служить исходной позицией для добавления элементов в конец файла}

Функция EOF(F) возвращает логическое значение true, если достигнут признак конца файла, или false в противном случае. Например, если файл использовался для чтения, то возникновение ситуации "конец файла" (и, соответственно; значение true, возвращаемое функцией EOF) означает, чтo все элементы файла прочитаны.

Процедура Truncate(F) используется для удаления части файла. начиная от текущей позиции указателя до окончания файла.

6.3.6 Обработка ошибок ввода-вывода.

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

Имеется возможность предусмотреть собственную реакцию на ошибочные ситуации в самой программе. Для этого необходимо в начале "опасного" фрагмента программы отключить автоматическую проверку на возникновение ошибки. Это делается внесением директивы компилятора {$I-} В этом случае возникновение ошибки при выполнении программы не будет приводить к немедленному ее завершению: вместо этого код ошибки (отличный от нуля) будет сохранен системой. Посредством стандартной функции IOResult без параметров можно получить этот код и построить дальнейшие действия в зависимости от его значения, например, вывести краткое сообщение об ошибке (без указания причины ошибки).

Assign (F, 'c:\myfiie' ); {$I-} {отключение автоматическою контроля}

Reset(F);

{$I+} { включение автоматического контроля }

if IOResult <> 0 then { возникла ошибка! }

begin

WriteLn('Ошибка при открытии файла для чтения');

WriteLn('Работа будет закончена');

WriteLn;

WriteLn('Haжмите Enter');

ReadLn;

Exit {Выход из процедуры}

end.

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

При необходимости можко провести 6олее детальный (расширенный) анализ кода ошибки, например

Assign (F, ' с: \myfile ');

{$I -} { отключаем автоматический контроль }

Reset(F);

Code :=IOResult; { получили код результата }

if Code <> 0 then

begin {возникла ошибка! }

Write(' Ошибка при открытии файла: ');

case Code of

1: WriteLn ('Файл не найден');

3: WriteLn ('Маршрут не найден');

4: WriteLn ('Слишком много открытых файлов');

5: WriteLn ('Доступ к файлу запрещен');

12 : WriteLn ('Некорректный код доступа к файлам')

else WriteLn(' Неизвестный код ошибки');

end

end;

{$!+} {включаем автоматический контроль }

Пример структуры программы.

program file_operation; {Программа для работы со списком сотрудников}

uses crt;

type

zap = record {Определение записи с полями «фамилия» - name,

«тарифная ставка» - tarif}

name : string[15];

tarif : Longint

end;

var

F1: file of zap;

s: zap;

с: char;

procedure out_fil; {Процедура сохранения данных в файле} var file_name : string;

begin

{Задание имени файла для сохранения} assign(F1, fiIe_name);

{Открытие файла для записи и контроль ошибок при выполнении этой операции} repeat

write('Введите фамилию или * (признак окончания ввода): '};

readln(s.name);

if s.name <> '*' then begin

writeln('Bведите тариф');

readln(s.tarif);

write(F1,s) end

until s.name = '*';

close(F1) end;

procedure IN_fil; {Процедура чтения данных из файла и вывода на экран} var file_name : string;

begin

{Задание имени файла для чтения} assign(F1, file_name);

{Открытие файла для чтения и контроль ошибок при

выполнении этой операции} writeIn('—— Список... ——');

{Заголовок списка}

repeat {Цикл чтения данных из файла и вывода на экран}

read(F1 ,s);

writeln(s.name, ' ',s.tarif)

until Eof(F1);

close(F1)

end;

begin {— Начало головной части программы —}

repeat { Цикл сохранения данных. Если при каждом обращении к процедуре Out_fil задавать разные имена файлов, то этот цикл позволит сохранить несколько списков; каждый список в своем файле}

clrscr;

Out_fil;

writeln('Закончить ввод списков ? (Y/N)');

readln;

until (с = 'Y') or (с='y');

repeat { Цикл чтения данных. Этот вариант позволяет просмотреть списки из разных файлов, имена которых задаются при каждом обращении к процедуре In_fil} clrscr;

ln_fil;

writeIn(' Закончить просмотр списков ? (Y/N)');

readln(c);

until (с = 'Y') or {c='y');

end. {Окончание программы}

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

Содержимое текстового файла рассматривается как последовательность символьных строк переменной длины, разделенных специальной комбинацией кодов, «конец строки» (13 или ODh), «перевод строки» (10 иди OAh). Текстовый файл завершается специальным кодом «конец файла» (26 или 1Аh).

Представителем текстового файла в Pascal-программе является переменная файлового типа, которая должна быть описана с указанием стандартного типа text; например,

var MyText: text;

Для текстовых файлов применимы вышеопределенные начальные и завершающие операции (Assign, Reset, Rewrite, Flush, Close).

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

Для записи в текстовый файл и чтения из него используются процедуры ReadLn и WriteLn. Эти процедуры осуществляют те же действия, что и соответствующие процедуры Read и Write, но после операций чтения и записи производят переход к следующей строке текстового файла.

Для контроля за окончанием текстового файла можно использовать функцию EOF.

Функция SeekEoLn(F) производит поиск конца текущей строки. Она пропускает все символы-разделители значений в строке (пробелы и табуляции) и устанавливает текущий указатель файла либо на конце строки (и тогда возвращает true), либо на первом значащем символе.

Функция SeekEoF(F) осуществляет поиск конца файла и действует аналогично функции SeekEoLn, во кроме символов-разделителей она пропускает символы концов строк (то есть переходит со строки на строку) в поисках кода конца файла. Если код 1Аh найден, то функция возвращает true, в противном случае - false.

6.3.8 Файлы без типа.

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

Var

Data : file;

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

Assign (Data, ‘MyData.dta’);

Reset (Data, 200);

Для организации обменов с нетипизированными файлами в языке Паскаль предназначены две стандартные процедуры: BlockRead и BlockWrite.

BlockRead (var F:file; var Buf; Count:word; var Result:word);

Здесь F – имя нетипизированного файла; Buf – переменная-буфер (нетипизированный параметр); Count – число записей, читаемых за один вызов; Result – возвращает число фактически прочитанных записей. Аналогично описывается стандартная процедура BlockWrite.

f : file;

Assign (f, ‘MyData.dta’);

ReWrite (f, SizeOf(Str));

for i:=0 to M-1 do BlockWrite (f, Image [i]^, 1);

Close (f);