Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Михалкович С.С. Основы программирования

.pdf
Скачиваний:
74
Добавлен:
19.05.2015
Размер:
556.43 Кб
Скачать

ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ

Федеральное государственное образовательное учреждение высшего профессионального образования «ЮЖНЫЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ»

С.С. Михалкович

Основы программирования

Файлы. Рекурсия

МЕТОДИЧЕСКИЕ УКАЗАНИЯ для студентов 1 курса

факультета математики, механики и компьютерных наук

Ростов-на-Дону

2007

3

Аннотация

Методические указания содержат лекции по темам «Файлы», «Рекурсия» курса «Основы программирования» для студентов направления «Информационные технологии» факультета математики, механики и компьютерных наук

Методические указания разработаны кандидатом физико-математических наук, доцентом кафедры алгебры и дискретной математики Михалковичем С.С.

Печатается в соответствии с решением кафедры алгебры и дискретной математики факультета математики, механики и компьютерных наук ЮФУ, протокол № 3 от 13 ноября 2006 г.

4

1 Файлы

Файл – это именованная область на диске, предназначенная для хранения информации. Основным достоинством файлов является возможность хранить данные между запусками программы. Кроме того, количество информации в файле может быть значительным, превышая объем оперативной памяти. Файлы подразделяются по двум признакам: по типу элементов и по способу доступа.

По типу элементов различают текстовые и двоичные (бинарные) файлы. Текстовые файлы предназначены для хранения текста и состоят из строк разной длины, разделяемых специальными невидимыми символами перехода на новую строку. В операционной системе Windows разделителем строк в текстовых файлах служит пара символов с кодами 13 и 10, идущих подряд. В системах Unix и Linux разделителем строк является символ с кодом 10. Будем называть эти символы маркером конца строки и обозначать EOLN (от англ. End Of Line). Двоичные файлы предназначены для хранения произвольной информации. В языке Паскаль существует две разновидности двоичных файлов – типизированные и бестиповые. Типизированные файлы состоят из элементов одного типа, что позволяет работать с ними как с массивами, обращаясь к элементам по индексу. Бестиповые файлы предназначены для низкоуровневой работы с файлами, и в данной книге рассматриваться не будут.

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

Перед началом работы с файлом его требуется открыть, а по окончании работы – закрыть. Если по окончании работы программы файл не закрыт, то часть записываемых в него данных может быть потеряна. С каждым открытым файлом связан так называемый файловый указатель – текущая позиция в файле, в которой будут производиться операции чтения/записи. При открытии файла файловый указатель обычно устанавливается на начало файла, а при каждой операции чтения/записи автоматически продвигается вперед на размер считанных/записанных данных.

5

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

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

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

Файловая переменная, связанная с текстовым файлом, в языке Паскаль имеет тип text, для типизированного файла – тип file of тип компонент, для бестипового файла – тип file. Перечислим основные подпрограммы для работы с файлами, общие для типизированных и текстовых файлов.

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

2.1 Основные процедуры и функции для работы с файлами

Assign(f,name) – процедура, связывающая файловую переменную f с файлом на диске с именем name. Вызывается до открытия файла. Не требует наличия файла на диске.

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

Rewrite(f) – процедура, создающая новый файл с именем, указанным в процедуре Assign, и открывающая его. Если файл уже есть, то он удаляется и создается пустой файл. Если файл по каким-либо причинам нельзя создать (например, имя файла содержит запрещенные символы), происходит ошибка времени выполнения. Для текстового файла открывает его на запись.

6

Close(f) – процедура, закрывающая открытый файл. Для неоткрытого файла ее вызов приводит к ошибке.

Eof(f)– функция, возвращающая True, если достигнут конец файла, и False в противном случае. Если достигнут конец файла, то есть файловый указатель стоит непосредственно за последним элементом файла, считается, что файловый указатель стоит на специальном элементе EOF (Eof – end of file), называе-

мом маркером конца файла.

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

Read(f,x1,x2,x3) – считывает данные из файла f в переменные x1, x2, x3. Для типизированных файлов тип переменных должен совпадать с типом элементов файла, для текстовых – переменные x1, x2, x3 могут иметь те же типы, что и в процедуре Read для ввода данных с клавиатуры (т.е. символьный, строковый или числовой). При этом данные, считываемые из текстового файла, должны храниться в том же виде, как при вводе с клавиатуры; в частности, при считывании числовых данных пропускаются лидирующие пробелы. Данные же, считываемые из типизированного файла, должны храниться в том же формате, в каком хранятся значения соответствующих типов в оперативной памяти.

Write(f,x1,x2,x3) – записывает данные в файл f из переменных x1, x2, x3. Для типизированных файлов тип переменных должен совпадать с типом элементов файла, для текстовых вместо переменных можно использовать любые выражения символьного, строкового или числового типа. В текстовый файл данные записываются в текстовом виде, а в типизированный – в том виде, в котором хранятся значения этих типов в оперативной памяти.

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

Erase(f) – удалить файл (файл должен быть закрыт). Rename(f,newname) – переименовать файл (файл должен быть закрыт).

2.2 Подпрограммы для работы с файлами в Delphi

Cледующие подпрограммы доступны в Delphi при подключении модуля

SysUtils:

FileExists(name) – функция, возвращающая True, если файл с именем name существует, и False в противном случае.

DirectoryExists(name) – функция, возвращающая True, если каталог с именем name существует, и False в противном случае.

7

DeleteFile(name) – функция, удаляющая файл с именем name и возвращающая True, если файл удален, и False в противном случае.

RemoveDir(name) – функция, удаляющая каталог с именем name и возвращающая True, если каталог удален, и False в противном случае.

GetCurrentDir – функция, возвращающая имя текущего каталога. SetCurrentDir(name) – процедура, устанавливающая каталог с именем

name текущим.

CreateDir(name) – процедура, создающая каталог с именем name. Если в переменной s типа string хранится полное имя файла, то

ExtractFilePath(s) – функция, возвращающая путь к файлу. ExtractFileName(s) – функция, возвращающая имя файла. ExtractFileExt(s) – функция, возвращающая расширение файла.

Например, для s='D:\MyPrograms\a.pas' функция ExtractFilePath(s) возвращает 'D:\MyPrograms\', функция ExtractFileName(s) возвращает 'a.pas' и функция ExtractFileExt(s) возвращает '.pas'.

Процедуры Assign, Rename и Close, а также тип text в Delphi имеют синонимы AssignFile, RenameFile, CloseFile и TextFile. Они были введены для устранения коллизии имен при совместном использовании с классами компонент, в которых имеются методы с именами Assign, Rename, Close,

Text.

2.3 Простые примеры: чтение и запись

Пример 1. Рассмотрим задачу записи всех целых чисел от 1 до 9 в файл. Приведем код для типизированных файлов:

var f: file of integer; i: integer;

begin

Assign(f,'a.dat');

Rewrite(f);

for i:=1 to 9 do write(f,i);

Close(f); end;

Вначале с файловой переменной f связывается файл на диске a.dat, затем файл создается и одновременно открывается, в цикле в него записываются числа, после чего файл закрывается.

Для текстовых файлов (тип text) код аналогичен, однако для разделения чисел в тексте между ними выводятся пробелы:

8

Assign(f,'a.txt');

Rewrite(f);

for i:=1 to 9 do if i<9 then

write(f,i,’ ’) else write(f,i); Close(f);

Несмотря на похожий код программ, содержимое этих файлов будет существенно различаться. Файл a.dat будет иметь размер, равный 9 элементам типа integer, то есть 36 байтам, и будет содержать целые числа в двоичном формате, в котором они хранятся в оперативной памяти. Просматривать содержимое такого файла лучше всего в специализированном редакторе (например, в программе FAR по F3, установив шестнадцатеричный способ отображения). Файл a.txt будет содержать числа в том виде, в котором они выводятся на экран:

1 2 3 4 5 6 7 8 9

Пример 2. Рассмотрим обратную задачу – считать из файла все числа и вывести их на экран. Пусть числа хранятся в файле a.dat в двоичном формате. Тогда следует использовать типизированные файлы и следующий программный код:

var f: file of integer; x: integer;

begin

Assign(f,'a.dat');

Reset(f);

while not Eof(f) do begin

Read(f,x); Write(x,' ');

end; Close(f);

end;

Если числа хранятся в файле a.txt в текстовом формате и разделены пробелами, то следует использовать текстовые файлы. Код программы будет почти таким же, за исключением того, что файловая переменная должна иметь тип text и связываться с файлом a.txt.

2.4 Обработка ошибок ввода/вывода

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

9

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

Пример 1. Составить функцию MyFileExists, проверяющую, существует ли файл с именем name.

function MyFileExists(name: string): boolean; var f: file;

begin

Assign(f,name); {$I-}

Reset(f); {$I+}

Result := IOResult=0 if Result then

Close(f);

end;

Отметим, что в модуле SysUtils имеется аналогичная функция FileExists. Второй способ обработки ошибок ввода/вывода использует механизм исключений. Если контроль над ошибками ввода/вывода включен (директива компилятора {$I+}, этот режим действует в Delphi по умолчанию), то генерируется ис-

ключение.

Пример 2. Составить функцию MyFileExists, используя механизм обработки исключений.

Для реализации этого способа контроль над ошибками ввода-вывода должен быть включен ({$I+}) и к программе должен подключаться модуль

SysUtils:

{$I+}

uses SysUtils;

function MyFileExists(name: string): boolean; var f: file;

begin

Assign(f,name); try

Reset(f);

Close(f);

Result:=True;

10

except

Result:=False; end;

end;

2.5 Типизированные файлы

Напомним, что для описания типизированного файла используется конструкция file of тип. При этом в качестве типа компонентов файла не может фигурировать файловый тип или тип длинных строк AnsiString (по умолчанию в Delphi string=AnsiString, поэтому конструкция file of string вызовет ошибку компиляции).

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

Truncate(f) – процедура, усекающая типизированный файл в позиции файлового указателя. Все элементы, начиная с текущего (то есть с элемента, на котором расположен файловый указатель), удаляются. Файл должен быть открыт.

FileSize(f) – функция, возвращающая количество элементов в типизированном файле.

FilePos(f) – функция, возвращающая позицию файлового указателя. Элементы нумеруются от нуля, так что номер последнего элемента равен

FileSize(f)-1.

Seek(f,i)– процедура, перемещающая файловый указатель на элемент с номером i.

Рассмотрим несколько примеров, иллюстрирующих основные действия с типизированными файлами.

Пример 1. Добавить число 0 в конец файла целых чисел a.dat (если файл не существует, то создать его).

Решение.

uses SysUtils; const name='a.dat';

var f: file of integer; x: integer;

begin

Assign(f,name);

if not FileExists(name) then Rewrite(f)

else Reset(f); Seek(f,FileSize(f)); x:=0;

write(f,x);

11

Close(f); end;

В данном примере следует обратить внимание на команду Seek(f,FileSize(f)), перемещающую файловый указатель за последний элемент, то есть на маркер конца файла.

Пример 2. Дан файл вещественных чисел. Возвести все его элементы в квад-

рат.

Решение 1. Всякий раз после считывания элемента будем возводить его в квадрат, возвращаться назад и записывать на старое место.

const name='b.dat'; var f: file of real;

x: real; begin

Assign(f,name);

Reset(f);

for i:=0 to FileSize(f)-1 do begin

read(f,x);

x:=x*x; Seek(f,FilePos(f)-1); write(f,x);

end; Close(f);

end;

В данном примере следует обратить внимание на команду Seek(f,FilePos(f)-1), перемещающую файловый указатель на предыдущий элемент.

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

const name='b.dat';

var f,f1: file of real; x: real;

begin

Assign(f,name);

Reset(f);

Assign(f1,'temp.dat');

Rewrite(f1);

for i:=0 to FileSize(f)-1 do begin

12