лабораторные работы паскаль. Практикум по программированию на языке Паскаль Учебное пособие
Скачать 0.89 Mb.
|
Тема: "Процедуры и функции" Цель работы 1. Приобретение практических навыков в программировании процедур и функций. 2. Изучение механизма передачи параметров. 3. Знакомство с локальными и глобальными переменными. Краткие сведения из теории Процедуры и функции являются мощным средством языка программирования. Это средство удобно применять в тех случаях, когда при решении задачи возникает необходимость в программе некоторую совокупность операторов повторять несколько раз. Так например, может возникнуть необходимость один и тот же цикл использовать в нескольких местах программы. Функцию или процедуру можно сравнить с мини-программой, именно поэтому их называют иногда одним общим именем - "подпрограмма (ПП)". ПП оформляется подобно программе: в начале записывается заголовок ПП, затем следует декларативная часть ПП и после процедурная. В декларативной части описываются все данные, область действия которых ограничена телом данной ПП. Эти данные называются локальными. Данные, объявленные в основной (главной) программе, называются глобальными и они могут использоваться в любой ПП, входящей в основную программу. В процедурной части описывается тело ПП, реализующее алгоритм решения, и которое заключается в операторные скобки BEGIN, END. ПП помещается сразу же после объявления всех переменных. Заголовок ПП для подпрограмм-функций начинается с ключевого слова FUNCTION, для подпрограмм- процедур с ключевого слова PROCEDURE. Эти ключевые слова играют роль признаков, которые распознает компилятор. Так как ПП исполняется не сразу и возможно не один раз, то компилятор, встретив тело ПП, должен его пропустить. 11.1. Функции Общая схема функции следующая: FUNCTION < идентификатор функции >(<параметр: тип параметра> [,<параметр: тип параметра>, ...]) : <тип результата функции>; < декларативная часть (разделы LABEL, CONST, TYPE, VAR ) -объявление локальных данных > BEGIN < процедурная часть функции - тело функции > END; В теле основной программы функция вызывается по имени, это значит, что после FUNCTION необходимо записать идентификатор функции, а затем в круглых скобках перечислить все параметры функции. Так как язык Паскаль сильно типизирован, то он требует, чтобы после каждого параметра был указан его тип. Результатом вычисления функции всегда получается одно значение. Поэтому после круглых скобок в заголовке функции необходимо указать тип результата, вычисляемого функцией. Ко всем функциям обращаются одинаково: в любом предложении программы они играют роль переменной. Самым простым и наглядным примером использования функций являются стандартные функции, например, функция LENGTH(St). Эта функция может применяться в программе всякий раз, когда необходимо вычислить длину стринга, в данном случае стринга St. Все стандартные функции входят в состав компилятора, то есть они описаны в теле самого компилятора. LENGTH - это идентификатор функции, St - аргумент функции. Заголовок этой функции может быть следующий: 77 FUNCTION Lenght(St : string) : byte; Тип результата, возвращаемого этой функцией, BYTE. Пример Напишем подпрограмму, позволяющую вычислять степенную функцию а n , причем n может иметь только положительные целые значения. В этом примере параметрами функции должны быть: основание и степень. { вычисление степенной функции } FUNCTION Degree(Base: real; Power: integer) : real; { обьявление локальных переменных i, Y i - параметр цикла, считающий степени; Y - промежуточная переменная, содержащая текущие значения степенной функции } VAR i : integer; Y : real; { тело функции или процедурная часть } BEGIN Y:= 1; For i:= 1 to Power do Y:= Y * Base; Degree:= Y; END; В основной программе обращаться к этой подпрограмме (функции) можно многократно, например: VAR B, X, Z : real; P : integer; BEGIN { основная или главная программа } B:= 1.5; P:= 3; Z:= Degree(B, P); { основание = 1.5, степень = 3 } WriteLn('Значение степенной функции = ', Degree(Х*2, 10)); END. Проследим работу функции по первому вызову. Значения переменных B и P: 1.5 и 3 передаются подпрограмме DEGREE. Однако после входа в подпрограмму мы эти переменные называем уже не B и P, а Base и Power. Имена Base и Power, фигурирующие в подпрограмме, являются просто "пустышками", замещаемыми при работе функции (вычислении) конкретными значениями. По терминологии ПП параметры, используемые в самой подпрограмме, называются формальными, а параметры, используемые в основной программе при ссылке к функции, называются фактическими. В нашем примере B и P - фактические параметры, Base и Power - формальные. Этот прием введения фактических и формальных параметров удобен тем, что при многократном вызове подпрограммы мы можем передавать различные параметры, как это сделано в представленном выше фрагменте программы: Degree(B, P) и Degree(Х*2, 10). Имена формальных и фактических параметров не обязаны совпадать, хотя если случайно они и оказываются одинаковыми, никаких проблем не возникнет. Однако необходимо, 78 чтобы типы формального и фактического параметров были согласованы (например, оба были типа INTEGER или REAL). При выполнении любой ПП в первую очередь происходит сопоставление формальных и фактических параметров. С этого момента ссылка на Base и Power в подпрограмме фактически означает обращение к В и Р. Следовательно, команды, вычисляющие степенную функцию, используют значения В и Р (1.5 и 3). Результат вычислений помещается в имя DEGREE, в этом и состоит механизм, посредством которого функция "посылает" свой ответ в основную программу. После возврата в основную программу результат, переданный функцией, назначается переменной Z. При этом значение, вычисленное функцией и помещаемое в DEGREE, имеет тот же тип, что и переменная Z. Структуру программы, содержащей подпрограмму-функцию, можно представить следующим образом: PROGRAM Main; CONST { декларативная . . . ; часть VAR главной . . . ; программы } { процедурная часть главной программы } FUNCTION Sample( . . . ) : . . .; { заголовок функции } VAR { декларативная часть подпрограммы } . . . ; { процедурная часть ПП } BEGIN { Sample } Sample:= . . . ; END; { Sample } BEGIN { главная программа } < любое количество вызовов функции Sample > END. { конец главной программы } 11.2. Процедуры Подпрограмму следует оформить в виде процедуры, если она предназначена для решения задачи одного из двух типов. Задача первого типа: требуется выполнить некую последовательность действий, не возвращая результирующего значения. Задача второго типа: требуется изменить значения одного или нескольких фактических параметров. Процедура, как и функция, помещается в конце декларативной части программы. Компилятор распознает процедуру по ключевому слову PROCEDURE. С этого слова начинается заголовок процедуры, после которого следует идентификатор процедуры, а затем в круглых скобках перечисляются формальные параметры процедуры. В Паскале передать параметры подпрограмме можно двумя способами. До сих пор мы имели дело только с одним из них. Этот способ называют передачей параметров по значению. Он состоит в том, что значение фактического параметра назначается соответствующему формальному параметру. Другими словами, перед началом выполнения процедуры вычисляется конкретное значение фактического параметра (например, 1.5 или 3). Затем полученное значение копируется в соответствующий формальный параметр, принадлежащий процедуре. Как только начинается выполнение процедуры, никакие изменения значения формального параметра уже не оказывают влияния на значение соответствующего фактического параметра. Это значит, что по окончании работы процедуры фактический параметр будет иметь точно такое же значение, каким он обладал до начала работы процедуры, вне зависимости от того, что происходило с формальным параметром. Такой способ передачи параметров действует при работе с функциями. 79 Второй способ передачи параметров называется передачей параметров по ссылке или по адресу. При передаче параметра по ссылке в процедуру пересылается уже не значение аргумента, а его местоположение (адрес) в памяти компьютера. Чтобы сообщить компилятору о намерении передать такой параметр, в заголовке процедуры в списке формальных параметров следует указать слово VAR. Если формальный параметр снабжен атрибутом VAR, а соответствующий ему фактический параметр является переменной, то любые изменения формального параметра будут отражаться в значениях фактического параметра, поскольку теперь формальный и фактический параметры занимают одну и ту же область памяти. Общая схема процедуры аналогична схеме функции со следующими изменениями: ключевое слово FUNCTION заменяется на PROCEDURE; отсутствует тип результата. Вызов процедуры в основной программе оформляется как отдельное предложение, состоящее из имени процедуры и пары круглых скобок, в которых через запятую перечислены фактические параметры. Предложение заканчивается как обычно символом ";". Рассмотрим пример на задачу первого типа: необходимо выполнить перемножение двух матриц А(3х4) и В(3х4). Оформим в виде процедуры ввод матрицы. Программа в этом случае может иметь вид: {*************************************************************} {* программа перемножения двух матриц A, B *} {*************************************************************} Program Main; Const N = 3; { количество строк в матрице } M = 4; { количество столбцов в матрице } Type MatrTyp = array[1..N, 1..M] of real; Var A, B, C : MatrTyp; { массивы можно передавать только как простой тип } i1, i2, i3 : byte; { индексы массивов } {*************************************************************} {* процедура ввода значений матрицы *} {*************************************************************} Procedure GetMatr(NameMatr : string; var Matr : MatrTyp); Var i, j : byte; Begin { GetMatr } WriteLn('Введите значения матрицы ', NameMatr); For i:= 1 to N do begin WriteLn('Строка ', i); For j:= 1 to M do Read(Matr[i,j]); WriteLn; { перевод на новую строку } end; End; { GetMatr } {*************************************************************} 80 {* главная программа *} {*************************************************************} Begin GetMatr('A', A); {Вызов процедуры для ввода значений матрицы А} GetMatr('B', B); {Вызов процедуры для ввода значений матрицы B} { алгоритм перемножения матриц } For i1:= 1 to N do For i2:= 1 to M do begin C[i1,i2]:= 0; For i3:= 1 to N do C[i1,i2]:= C[i1,i2] + A[i1,i2] * B[i3,i1]; end; { вывод значений результирующей матрицы } For i1:= 1 to N do begin For i2:= 1 to M do Write(' C[',i1:1, ',', i2:1, '] = ', C[i1,i2]:8:3); WriteLn; { перевод на новую строку } end; End. { конец главной программы } 11.3. Различия между процедурами и функциями Главное различие (из которого следуют все остальные) состоит в том, что функция всегда возвращает, причем в явной форме, одно-единственное значение, которое может быть использовано в качестве составной части выражения; процедура такого значения не возвращает. Однако применительно к процедуре все же можно говорить о возвращаемой информации - процедура способна изменять значения своих параметров (тех, что описаны с атрибутом VAR). Помимо главного различия можно отметить ряд второстепенных различий синтаксического характера. Так, например, заголовок функции всегда завершается указанием типа возвращаемого значения. В заголовке процедуры такая информация не нужна. Для функции типично, чтобы в качестве последнего шага имени функции было назначено некоторое вычисленное значение. В процедурах этого нет. И наконец, еще одно различие. Поскольку функция возвращает какое-то значение, вызов функции может появляться прямо в выражении. Вызов процедуры не может быть частью выражения - это всегда отдельное предложение. Контрольные вопросы 1. Для чего предназначены функции? 2. Для чего предназначены процедуры? 3. Чем отличаются формальные и фактические параметры? 4. Опишите способы передачи параметров в подпрограммы и их особенности? 4. Что включает в себя заголовок подпрограммы? 5. Чем отличаются глобальные и локальные переменные? 6. Какая разница между процедурой и функцией? Задание к работе 1. Модифицируйте подпрограмму, вычисляющую степенную функцию так, чтобы она вычисляла и отрицательные степени. 2. Напишите подпрограмму, способную вычислять любые степени: положительные и отрицательные, целочисленные и действительные. 3. Выполните индивидуальное задание: 81 Дано несколько массивов чисел. Длины массивов заданы в варианте индивидуального задания. Требуется в каждом массиве найти наибольший и наименьший элементы и отобразить их на экране, затем все компоненты каждого массива возвести в квадрат и снова найти наибольший и наименьший элементы. Вычисление максимальной и минимальной величин оформить в виде процедуры, глобальные параметры в процедуре не использовать. Методические указания 1. При выполнении пункта 2 задания необходимо использовать экспоненциальную и логарифмическую функции. 2. При выполнении пункта 3 задания необходимо знать, что: a) если в качестве исходной информации в процедуру передается массив, то его следует передавать по ссылке для экономии памяти, так как в этом случае при вызове процедуры не образуется локальный массив; b) несмотря на то, что обрабатываемые массивы разной длины, они описываются в программе как массивы одного и того же типа, так как при обращении к процедуре типы соответствующих формальных и фактических параметров должны совпадать. 3. Составить алгоритм решения задачи. 4. Написать программу и откомпилировать ее. 5. Составить контрольный тест и отладить (протестировать) программу. 6. Составить отчет и представить его к защите. Содержание отчета 1. Титульный лист. 2. Словесная постановка задачи. 3. Графический или текстуальный алгоритм решения задачи. 4. Листинг программы. 5. Контрольный тест и результаты тестирования программы. 6. Ответы на контрольные вопросы. Варианты индивидуальных заданий Ввести и обработать: 1) два двумерных массива, содержащие соответственно 3х5 и 4х8 вещественных элементов; 2) три массива, содержащие соответственно 3, 6 и 8 целых элементов без знака; 3) четыре массива, содержащие соответственно 4, 6, 3 и 5 целых элементов со знаком; 4) два массива, содержащие соответственно 4 и 6 вещественных элементов; 5) три массива, содержащие соответственно 5, 10 и 4 целых элементов без знака; 6) четыре массива, содержащие соответственно 3, 5, 8 и 6 вещественных элементов; 7) два трехмерных массива, содержащие соответственно 2х3х2 и 3х4х2 вещественных элементов; 8) четыре массива, содержащие соответственно 4, 7, 3 и 5 вещественных элементов; 9) три двумерных массива, содержащие соответственно 2х5, 3х6 и 3х4 целых элементов без знака; 10) два двумерных массива, содержащие соответственно 6х2 и 3х2 вещественных элементов. 82 ЛАБОРАТОРНАЯ РАБОТА N 12 Тема: " Работа с файлами" Цель работы 1. Изучение файловых типов в языке Турбо-Паскаль. 2. Получение навыков в организации файлов и использовании их для обработки информации. Краткие сведения из теории До сих пор данные вводились в программу через клавиатуру, т.е. с непременным участием человека. Такой способ ввода информации называется интерактивным режимом ввода. Возможен и иной подход, основывающийся на использовании набора данных, подготовленных заранее и хранящихся в виде файла на магнитном носителе. Иначе говоря, альтернативой интерактивному режиму является такой способ ввода, при котором информация поступает из источника, физически существующего вне программы. Этот процесс обычно называют считыванием данных из внешнего файла (или просто из файла). Указанный способ находит широкое применение при обработке информационных массивов весьма значительного объема, когда интерактивный режим становится слишком обременительным и малоэффективным. Вторым мотивом использования файлов является то, что он может быть создан какой-то другой программой. Таким образом, файл становится связующим звеном между разными задачами. Наконец, последнее немаловажное соображение: если входные данные поступают в программу из внешнего файла, то присутствие пользователя в момент фактического исполнения программы становится необязательным. Файл - это именованная область внешней памяти компьютера (жесткого диска, гибкой дискеты,...), содержащая логически связанную совокупность данных. На языке Паскаль можно создавать три типа файлов : текстовый, типизированный, нетипизированный, которые в программе (в разделе VAR) объявляются следующим образом: < файловая переменная > : text; < файловая переменная > : file of < тип компоненты >; < файловая переменная > : file. < файловая переменная > - это логическое имя файла, используемое в программе. < тип компоненты > - компонентой файла может быть как переменная базового типа, так и структурного. Структурный тип определяет данные типа "запись" (record). "Запись" - это логически связанная совокупность нескольких данных. К данным типа RECORD можно обращаться как ко всей совокупности данных, так и к ее отдельным данным (элементам). Для обращения к отдельным элементам "записи" используется уточненное имя. Уточненное имя состоит из идентификатора "записи", десятичной точки и идентификатора элемента записи. В свою очередь каждый элемент "записи" может быть "записью". Тогда, для обращения к внутреннему элементу необходимо последовательно перечислить через десятичную точку все идентификаторы иерархически вложенных "записей", начиная от внешнего имени к внутреннему, последним в этой последовательности является идентификатор самого элемента. При использовании структурного типа компоненты "запись" необходимо объявлять его в программе в разделе TYPE. Пример: ТYPE { запись данных по студенту } RecFile = RECORD { признак начала записи } Fam, Name, Otch : string[15]; 83 GodR : word; Ngrup : string[10]; END; { конец записи } VAR F1 : Text; F2 : File of byte; F3 : File of RecFile; F4 : File; Buf : RecFile; {_2буфер ввода-вывода_0, в который считываются данные компоненты файла} В этом фрагменте программы F1- файловая переменная текстового типа, F2 и F3 - файловые переменные типизированного типа, причем F2 может содержать только байтовые компоненты, а в файле F3 каждая компонента представляет из себя "запись" из трех элементов, F4- файловая переменная нетипизированного типа. Уточненные имена элементов записи Buf: Buf.Fam, Buf.Name, Buf.Otch, Buf.GodR, Buf.NGrup. 12.1 Доступ к файлам В первую очередь при работе с файлами необходимо привести в соответствие файловую переменную и имя файла, с которым он хранится на внешнем носителе. С этим именем работает операционная система(ОС) DOS. Соответствие устанавливается с помощью процедуры: ASSIGN (< ф.п.>, < имя файла или л.у.>); Здесь < ф.п.> - файловая переменная; < имя файла или л.у.> - это полное имя файла, которое включает в себя путь доступа, непосредственно имя файла и расширение. "л.у." - стандартное логическое устройство. Например: ASSIGN (F1, 'a:\Tp5\DAT\St629.DAT'); ASSIGN (F2, 'Dannye.DAT'). Если не указан путь к файлу, то запись или считывание осуществляется с текущего директория или в текущий директорий. В качестве имени файла в процедуре ASSIGN можно указывать логическое устройство из следующего списка: CON, PRN, AUX. CON - это имя консоли. На персональном компьютере (ПК) под консолью понимается совокупность двух устройств: клавиатуры и дисплея. Клавиатура используется для ввода информации, а дисплей для вывода. PRN - это стандартное имя принтера. В ОС PRN стандартно назначается LPT1. В модуле Printer Турбо-Паскаля объявлена файловая переменная Lst. Поэтому при отображении данных на принтер, достаточно подключить модуль Printer к программе, а в предложениях Write и Writeln первым аргументом записать имя файловой переменной Lst: Write (Lst, < список выводимых данных >); Пример вывода информации без использования модуля Printer : VAR F : File; BEGIN Assign(F, PRN); ReWrite(F); Writeln(F, 'Пример использования Л.У. - PRN'); Close(F); END; AUX - это имя коммуникационного порта. Обычно их бывает два у ПК: СОМ1 и СОМ2. Стандартно AUX назначается СОМ1. Этот порт обычно используется для подключения нестандартных устройств. Например, "мыши", дигитайзера, графопостроителя и т.п. 84 12.2 Инициация файла и завершение работы с ним Прежде чем начать обработку файла необходимо выполнить некоторые операции по работе с устройством, на котором хранится или будет храниться файл. Так например, при создании файла необходимо: выделить область памяти на внешнем устройстве, в которую будут записываться данные файла; запомнить имя файла и адрес этой области. Если предстоит работа с файлом, уже существующим на внешнем носителе, то необходимы следующие действия: по указанному имени файла найти адрес, с которого записаны данные этого файла; установить головку устройства на начало файла. Эта совокупность операций называется инициацией файла или "открытием" файла. Инициируется файл с помощью процедур Reset и ReWrite. С помощью процедуры Reset инициируется, т.е. открывается ранее созданный файл. С помощью процедуры ReWrite инициируется файл для записи, то есть вновь создаваемый файл. Синтаксис: Reset(< ф.п.> [,< размер записи в байтах >]); ReWrite(< ф.п.> [,< размер записи в байтах >]); Второй аргумент указывается только для нетипизированных файлов. Текстовые файлы можно инициировать также и процедурой Append: Append(< ф.п.>); В этом случае ранее созданный файл открывается для добавления данных в конец файла. Завершив работу с файлом, необходимо его закрыть. При закрытии файла ОС подсчитывает размер файла в байтах и запоминает его. Кроме того, запоминается также информация о дате и времени создания файла или его последней модификации (корректировки). Закрытие файла данных осуществляется процедурой Close: Close(< ф.п.>); При считывании данных из ранее созданного файла конец файла можно определить с помощью функции EOF: EOF(< ф.п.>); Эта функция имеет значение TRUE при считывании маркера конца файла. В противном случае она будет иметь значение FALSE. Данная функция обычно используется для организации цикла по чтению всех компонент файла: while not EOF(F1) do begin < считывание и обработка компонент файла > end; 12.3. Считывание данных из файла и запись их в файл Непосредственный ввод информации осуществляется предложениями READ и READLN, а вывод ( запись) информации - WRITE и WRITELN. Особенностью их применения к файлу является обязательность указания файловой переменной в качестве первого параметра в списке элементов ввода или вывода: Read(< файловая переменная >, < список ввода >); ReadLn(< файловая переменная >, < список ввода >); Write(< файловая переменная >, < список вывода >); WriteLn(< файловая переменная >,< список вывода >). 12.4. Текстовые файлы 85 Текстовый файл - это совокупность строк переменной длины. Переменная длина строк определяет наличие маркеров, которые отмечают конец строки. В качестве маркеров используются два управляющих символа "Перевод строки" и "Возврат каретки", их десятичные коды: #10, #13. Названия управляющих символов "Перевод строки"(LF - Line Feed) и "Возврат каретки"(CR - Carriage Return) взяты по аналогии работы с пишущей машинкой. Конец строки можно определить с помощью функции EOLn: EOLn(< ф.п.>); Для записи данных в файл используются процедуры WRITE и WRITELN: Write(< ф.п.>, < список вывода стрингов >); Writeln(< ф.п.>, < список вывода стрингов >). По предложению WRITE значения данных из списка запишутся в файл подряд без всяких разделителей. Поэтому программист, используя предложение WRITE, должен позаботиться о разделителях между данными, если они нужны. По предложению WRITELN в файле после каждого выведенного стрингового значения будут записаны признаки конца строки. Для чтения данных из файла используются процедуры READ и READLN: Read(< ф.п.>, < список вводимых стрингов >); Readln(< ф.п.>, < список вводимых стрингов >); По предложению READ из файла выбирается столько символов, сколько указано в описании текущего стринга, принадлежащего списку ввода. Выбранная последовательность символов присваивается текущему стрингу. Эта совокупность операций повторяется для всех элементов списка ввода. По предложению READLN из файла последовательно считываются строки и присваиваются стрингам из списков. Если выбранная строка имеет большее количество символов, чем указано в описании текущего стринга, то она обрезается до указанной длины, при этом часть информации теряется. Поэтому необходимо следить за соответствием длин стрингов, записываемых в файл и считываемых из файла. Пример : Var Fio, Otch : string[15]; Name : string[10]; i : integer; F : text; Begin Assign(F, 'St629.DAT'); { файл будет создаваться в текущем каталоге } { создание файла или первичная запись данных в файл} ReWrite(F); { открытие файла для записи } for i:=1 to 5 do { ограничимся вводом пяти студентов } begin Write('Фамилия: '); Readln(Fam); Write('Имя: '); Readln(Name); Write('Отчество: '); ReadLn(Otch); Write(F, Fam, Name, Otch); end; close(F); { чтение данных из файла и вывод их на экран } WriteLn(' Фамилия Имя Отчество'); Reset(F); { открытие существующего файла } for i:=1 to 5 do begin 86 Read(F, Fam, Name, Otch); Write(Fam:16, Name:11, Otch:15); end; close(F); End. 12.5. Типизированные файлы Компоненты этого файла могут быть следующих типов: базового: byte, word, longint, integer, real, запись, char, string; структурного; регулярного. При этом все компоненты файла имеет один и тот же тип. Это означает, что длина компоненты фиксирована. Объявляется такой файл в программе следующим образом : Var F1 : File of byte; F2 : File of string[80]; F3 : File of real; F : File of RecFile; Здесь F1, F2, F3, F - это файловые переменные, которые указывают на файлы, компонеты которых соответственно являются типа byte, string, real и record. Чтение компонент файла выполняется процедурой: Read(< ф.п.>, < список ввода >); Запись компонент в файл выполняется процедурой: Write(< ф.п.>, < список вывода >); Пример: Var X, Y : array[1..100] of integer; { массивы координат } F : file of real; i : byte; Begin < операторы по вводу 100 значений координат X, Y > Assign(F, 'Coor.dat'); { файл будет создаваться в текущем каталоге } ReWrite(F); { открытие файла для записи } For i:= 1 to 100 do Write(F, X[i], Y[i]); { запись координат в файл } Close(F); End. В приведенном фрагменте программы координаты записаны последовательными парами X, Y. При такой организации файла происходит частое обращение к внешнему носителю, это приводит к замедлению работы программы, что особенно заметно при работе с большими объемами данных. Поэтому рекомендуется данные записывать в файл и считывать из файла большими блоками, примерно кратными 512 байтам. Согласно этому модифицируем программу следующим образом: Type Coord = array[1..100] of integer; { массивы координат } Var 87 X, Y : Coord; { массивы координат } F : file of Coord; { файл регулярного типа } i : byte; Begin < операторы по вводу 100 значений координат X, Y > Assign(F, 'Coor.dat'); { файл будет создаваться в текущем каталоге } ReWrite(F); { открытие файла для записи } Write(F, X); { запись массива координат Х в файл } Write(F, Y); { запись массива координат Y в файл } Close(F); End. Теперь в файле сначала записаны 100 координат Х, а затем 100 координат Y. Данные файла мы записали двумя большими блоками по 600 байтов каждый. Следует помнить, что это не самый лучший способ организации файла для данных примера, но достаточно понятный. Считывание координат из файла выполняется аналогично, в программе нужно вместо процедуры REWRITE использовать процедуру RESET, а вместо предложения WRITE использовать предложение READ. Хорошо структурируемые данные, например, данные о каком-либо объекте, удобно описывать типом "запись". В этом случае компонента файла будет структурного типа. Пример: Type RecFile = record { запись данных по студенту } Fam, Name, Otch : string[15]; GodR : word; NGrup : string[10]; end; Var i : integer; Buf : RecFile; FilStud : file of RecFile; Begin Assign(FilStud, 'Stud.dat'); ReWrite(FilStud); WriteLn('Введите данные по студентам:'); For i:= 1 to 10 do { ограничимся 10 записями } begin { интерактивный ввод данных } Write('Фамилия : '); Readln(Buf.Fam); Write('Имя : '); Readln(Buf.Name); Write('Отчество : '); Resdln(Buf.Otch); Write('Год рождения : '); Readln(Buf.GodR); Write('N группы : '); Readln(Buf.NGrup); { запись данных в файл } Write(FilStud, Buf); { Buf - обращение ко всей записи } end; Close(FilStud); End. Чтение компонент типизированного файла можно осуществлять как последовательным, так и прямым методом доступа. 88 Последовательный доступ - это есть доступ к компоненте файла только после перебора всех предыдущих. Прямой доступ - это есть доступ сразу к указанной компоненте. Так как типизированные файлы обладают компонентами фиксированной длины, существует возможность организовать прямой доступ. Для организации прямого доступа к компонентам файла существуют стандартные процедуры Seek, FilePos, FileSize : Seek(< файловая переменная >,< номер компоненты >); FilePos(< файловая переменная >); FileSize(< файловая переменная >). Процедура Seek осуществляет прямой доступ к любой компоненте файла. Здесь < номер компоненты > - позиция указателя компонент файла. Она может принимать следующие значения: +1 - установить указатель на следующую компоненту; -1 - установить указатель на предыдущую компоненту; i - установить указатель на i-ую компоненту. Процедура FilePos определяет номер текущей позиции в файле, а точнее номер текущей компоненты. Процедура FileSize определяет размер указанного файла - количество компонент. Нумерация компонент начинается с нуля. Пример. Type RecFile = record { запись данных по студенту } Fam, Name, Otch : string[15]; GodR : word; NGrup : string[10]; end; Var i : integer; Buf : RecFile; FilStud : file of RecFile; Begin Assign(FilStud, 'Stud.dat'); Reset(FilStud); i:= FileSize; WriteLn('В файле ', i, ' компонент'); Seek(FilStud, i-1); { встали перед последней записью } Read(FilStud, Buf); { прочитали ее } { можно ее скорректировать и записать вновь в файл } Buf.GodR:= '1973'; { перед записью нужно вновь установить указатель перед этой записью } Seek(FilStud, -1); Write(FilStud, Buf); Close(FilStud); End. Примечание: Открывая типизированный файл процедурой RESET, можно этот файл не только читать, но и записывать в него новую информацию. При этом файл должен уже существовать на диске. 12.6. Нетипизированные файлы Нетипизированные файлы могут содержать в своем составе любые типы компонент. При этом правильность записи и считывание этих компонент полностью возлагается на 89 программиста. Длина компонент может быть различной. Для открытия нетипизированных файлов используются процедуры Reset, ReWrite: Reset(< файловая переменная >, < max размер буфера >); ReWrite(< файловая переменная >, < max размер буфера >). Так как за одно обращение к нетипизированному файлу можно считывать не одну компоненту, а несколько, и так как длины компонент могут быть различны, то в процедурах Reset и ReWrite указывается максимальный размер буфера ввода-вывода в байтах. Чтение компонент из файла и запись их в файл выполняется процедурами BlockRead и BlockWrite: BlockRead(< файловая переменная >, < буфер >,< кол-во компонент, считываемых за один раз >, [,< кол-во считанных компонент >]); BlockWrite(< файловая переменная >,< буфер >,< кол-во записываемых компонент >, [,< кол-во записанных компонент >]). Четвертый параметр необязателен. Он формируется системой и используется для проверки правильности завершения операций чтения или записи. Нетипизированные файлы рекомендуется использовать для организации эффективной работы с файлами, так как они позволяют работать, во-первых, с компонентами различной длины, и, во-вторых, с переменным числом обрабатываемых компонент. 2.7. Процедуры и функции для работы с файлами Все рассматриваемые функции и процедуры принадлежат стандартному модулю DOS, поэтому его необходимо подключить к программе с помощью предложения USES. 1. ReName(< файловая переменная >,< новое имя файла >) - переименование файла. 2. Erase(< файловая переменная >) - удаление файла. 3. ChDir(< путь >) - изменение директория, где <путь> - путь к новому директорию. 4. GetDir(< устройство >, < директорий >) - определение текущего каталога, где <устройство> задается следующим образом: 0 - текущее устройство; 1 - устройство A; 2 - устройство B и т.д. 5. MkDir(< директорий >) - создание нового каталога. В аргументе <директорий> указывается полный путь до того каталога, который создается. 6. PmDir(< директорий >) - удаление каталога. В качестве аргумента указывается полный путь до удаляемого каталога. При этом удаляемый каталог должен быть обязательно пустым. 7. IOResult - проверка правильности завершения работы той или иной операции ввода- вывода. Эта функция имеет тип WORD и возвращает значение 0, если операция ввода- вывода выполнилась успешно, и в противном случае следующие значения: 1 - файл не найден, 2 - путь не найден, 3 - слишком много открытых файлов, 5 - запрет доступа к файлу, 12 - некорректный код доступа к файлу и так далее. При применении этой функции в программе необходимо с помощью директивы компилятора отключить стандартную проверку - {$I-}, а после выполнения операций ввода-вывода включить - {$I+}. Данная функция записана в стандартном модуле SYSTEM. 8. DiskFree(< устройство >) - определение числа свободных байтов на заданном диске. Эта функция типа LONGINT. 90 В качестве аргумента указывается номер устройства. Если указано несуществующее устройство, то вместо объема свободной памяти на диске эта функция возвращает значение -1. Функцию рекомендуется применять перед созданием файла, чтобы выяснить, достаточно ли места для создаваемого файла на указанном накопителе. 9. DiskSize(< устройство >) - определение числа свободных байтов на диске. Тип функции LONGINT. Аргумент задается так же, как и в предыдущей функции. 10. FindFirst(< уточненное имя файла>, < атрибуты >, < доп.инф-я >) - поиск указанного файла. В процедуре входным параметром является только первый. Два последних параметра являются выходными. Параметр < атрибуты > имеет тип BYTE, параметр < дополнительная информация > должен быть объявлен как SearchRec. Этот тип описан в стандартном модуле Dos. 11. FindNext(< следующий файл >) - поиск указанного файла. Процедуры FindFirst и FindNext зачастую используются для просмотра всех файлов, находящихся в каталоге. 12. FSearch(< имя файла>,<список каталогов >) - поиск файла в списке каталогов. Функция имеет тип PathStr (описана в стандартном модуле Dos). 13. FSplit(< уточненное имя файла >, < путь >,< имя >,< расширение >) - выделение из уточненного имени файла трех переменных: < путь >, < имя файла >, < расширение >. 14. FExpand(< имя файла >) - добавление к имени файла, находящегося в текущем каталоге, полного пути доступа к нему. Примечание: перед использованием первых четырех процедур файл должен быть обязательно закрыт. Контрольные вопросы 1. Укажите режимы ввода информации. 2. В каких случаях удобно использовать файлы? 3. Дайте определение файла и укажите его характеристики. 4. Что такое путь доступа к файлу? 5. Где хранятся файлы ? 6. Выведите формулу подсчета объема файла в байтах. 7. Каким образом описываются переменные файловых типов ? 8. Как подразделяются файлы по видам доступа к его компонентам ? Как осуществляется доступ к компонентам файлов ? 9. Какие операции определены над файлами ? Задание к работе Задание А. Разработать программу в соответствии с вариантом задания, которая должна выполнять следующие функции: создание файла; чтение данных из файла; вывод считанных данных на экран дисплея. Задание Б . В программу, разработанную по заданию А, добавить блок обработки данных, инцидентных файлу, в соответствии с индивидуальным заданием. Все полученные результаты отобразить на экране. Методические указания 1. При разработке процедуры создания файла необходимо придерживаться следующей схемы действий: a) проверить с помощью процедуры DISKSIZE, есть ли место на диске; b) проверить, нет ли файла с таким же DOS - им именем на диске (процедура FINDFIRST, FINDNEXT); c) привести в соответствие DOS - ое имя файла с файловой переменной, используемой в программе (процедура ASSIGN); 91 d) открыть файл (процедура REWRITE); e) ввести данные, предназначенные для записи в файл; f) записать данные в файл (предложения WRITE / WRITELN); g) закрыть файл (процедура CLOSE). 2. При создании процедуры чтения необходимо: a) проверить, существует ли такой файл на диске (процедура FINDFIRST, FINDNEXT); b) если файл не существует, то необходимо уточнить имя в интерактивном режиме и снова перейти к пункту а); c) если файл существует, привести в соответствие DOS - ое имя файла с файловой переменной, используемой в программе (процедура ASSIGN); d) открыть файл (процедура RESET); e) считать данные из файла (предложения READ / READLN); f) отобразить считанные данные на экране дисплея; g) закрыть файл (процедура CLOSE). 3. В начале каждой процедуры необходимо: a) отключить стандартную проверку выполнения операций ввода-вывода, используя директиву компилятора {$I-}; b) после выполнения каждой операции ввода-вывода самостоятельно проверять код ее завершения с помощью функции IORESULT; c) при неуспешном завершении операции ввода-вывода устранить причину , приведшую к этой ситуации. Содержание отчета 1. Титульный лист. 2. Словесная постановка задачи. 3. Математическая постановка задачи. 4. Таблица идентификаторов входных и выходных данных, а также их типов. 5. Листинг программы. 6. Контрольный тест. 7. Результаты тестирования. 8. Анализ допущенных ошибок. 9. Инструкция по эксплуатации программы. 10. Ответы на контрольные вопросы. Варианты индивидуальных заданий Вариант 1 А. Создать файл, содержащий сведения о месячной заработной плате рабочих завода. Каждая запись содержит поля - фамилия рабочего, наименование цеха, размер заработной платы за месяц. Количество записей - произвольное. Б. Вычислить общую сумму выплат за месяц по цеху Х, а также среднемесячный заработок рабочего этого цеха. Вывести ведомость для начисления заработной платы рабочим этого цеха. Вариант 2 А. Создать файл, содержащий сведения о количестве изделий, собранных сборщиками цеха за неделю. Каждая запись содержит поля - фамилия сборщика, количество изделий, собранных им ежедневно в течение шестидневной недели (в понедельник, вторник и т.д.). Количество записей - произвольное. Б. По каждому сборщику просуммировать количество деталей, собранное им за неделю. Определить сборщика, собравшего наибольшее число изделий, и день, когда он достиг наивысшей производительности труда. Вариант 3 92 А. Создать файл, содержащий сведения о количестве изделий категорий А, В, С, собранных рабочим за месяц. Структура записи имеет поля - фамилия сборщика, наименование цеха, количество изделий по категориям, собранных рабочим за месяц. Количество записей - произвольное. Б. Считая заданными значения расценок Sа, Sb, Sc за выполненную работу по сборке единицы изделия категорий A, B, C соответственно, подсчитать: - общее количество изделий категорий А, В, С, собранных рабочим цеха Х; - ведомость заработной платы рабочих цеха Х; - средний размер заработной платы работников этого цеха. Вариант 4 А. Создать файл, содержащий сведения о телефонах абонентов. Каждая запись имеет поля - фамилия абонента, год установки телефона, номер телефона. Количество записей - произвольное. Б. По вводимой фамилии абонента выдать номер телефона. Определить количество установленных телефонов с ХХХХ года. Номер года вводится с терминала. Вариант 5 А. Создать файл, содержащий сведения об ассортименте игрушек в магазине. Структура записи - название игрушки, цена, количество, возрастные границы, например 2 ÷ 5, т.е. от двух до пяти лет. Количество записей - произвольное. Б. Найти игрушки, которые подходят детям от 1 до 3 лет. Определить стоимость самой дорогой игрушки и ее наименование. Определить игрушку, которая по стоимости не превышает Х руб. и подходит ребенку в возрасте от А до В лет. Значения Х, А, В ввести с терминала. Вариант 6 А. Создать файл, содержащий сведения о сдаче студентами первого курса сессии. Структура записи - индекс группы, фамилия студента, оценки по пяти экзаменам, признак участия в общественной работе: "1" - активное участие, "0" - неучастие. Количество записей - 30. Б. Зачислить студентов группы Х на стипендию. Студент, получивший все оценки "5" и активно участвующий в общественной работе, зачисляется на повышенную стипендию (доплата 50 %), не активно участвует - доплата 25 %. Студенты, получившие "4" и "5" , зачисляются на обычную стипендию. Студент, получивший одну оценку "3", но активно занимающийся общественной работой, также зачисляется на стипендию, в противном случае зачисление не производится. Индекс группы вводится с терминала. Вариант 7 А. Создать файл, содержащий сведения о сдаче студентами сессии. Структура записи - индекс группы, фамилия студента, оценки по пяти экзаменам и пяти зачетам ( "З" означает зачет, "Н" - незачет ). Количество записей - 25. Б. Определить фамилии неуспевающих студентов с указанием индексов групп и количества задолженностей. Найти средний балл, полученный каждым студентом группы Х, и всей группой в целом. Вариант 8 А. Создать файл, содержащий сведения о личной коллекции книголюба. Структура записи - шифр книги, автор, название, год издания, местоположение ( номер стеллажа, шкафа и т.д.). Количество записей - произвольное. Б. Найти: 1) местонахождение книги автора Х названия Y; 2) список книг автора Z, находящихся в коллекции; 3) число книг издания ХХ года, имеющееся в библиотеке. Значения Х, Y, Z, XX ввести с терминала; Вариант 9 93 А. Создать файл, содержащий сведения о наличии билетов и рейсах Аэрофлота. Структура записи - номер рейса, пункт назначения, время вылета, время прибытия, количество свободных мест в салоне. Количество записей - произвольное. Б. Найти время отправления самолетов в город Х; наличие свободных мест на рейс в город Х с временем отправления Y. Значения Х, Y вводятся по запросу с терминала. Вариант 10 А. Создать файл, содержащий сведения об ассортименте обуви в магазине фирмы. Структура записи - артикул, наименование, количество, стоимость одной пары. Количество записей - произвольное. Артикул начинается с буквы Д для дамской обуви, М для мужской, P для детской. Б. Определить наличие в файле обуви артикула Х, узнать ее стоимость; ассортиментный список дамской обуви с указанием наименования и имеющегося в наличии числа пар каждой модели. Значение Х вводится по запросу с терминала. |