Любой язык программирования высокого уровня должен содержать средства для организации хранения информации на внешних запоминающих устройствах и доступа к этой информации. Особенность этих средств заключается в существенно разных способах организации работы на различных вычислительных системах. Поэтому обычно в языках высокого уровня описываются только базовые понятия, а подробности работы детализируются в конкретных версиях языка.
В стандартном Паскале под файлом подразумевается заранее не определенное количество компонентов одинакового типа с общим именем. Порядок компонентов определяется самой последовательностью. Это – основное отличие файлов от предыдущих структур данных.
В любой момент времени доступен только один компонент файла, доступ же к другим производится последовательным продвижением по файлу. То, как выбираются компоненты, зависит от организации внешнего устройства, свойств операционной системы, и некоторых других причин, однако считается, что только некоторые компоненты присутствуют в оперативной памяти в определенный момент времени, причем только компонент, на который указывает буферная переменная, доступен непосредственно.
Таким образом, с каждым описанием новой файловой переменной (непосредственно файла) автоматически вводится дополнительная скрытая переменная с таким же типом, как тип компонент, называемая буферной переменной файла или текущим указателем файла.
Описание файловой переменной производится следующим образом.
TYPE <имя> = FILE OF <тип_компонент>
VAR <список_имен>: FILE OF <тип_компонент>
Имя файловой переменной назначается независимо от имен файлов, используемых операционной системой, по правилу образования имен Паскаля. Компоненты могут быть любого типа, кроме файлового, или комбинированного типа, если одним из его полей является файл. Например:
Var Fil: File of real;
Авторский вариант языка содержит минимальный набор допустимых действий с файлами, который лишь частично совпадает с операциями, реализованными в Турбо Паскале. Здесь, в частности, отсутствуют явные операции над буферной переменной (в этом пункте есть несовместимость стандартного и Турбо-Паскаля). Все операции с файлами реализованы в виде процедур и функций, что является общепринятым подходом в большинстве языков программирования.
Все операции с файлами можно условно разбить на 4 группы.
-
установочные и завершающие операции;
-
собственно ввод-вывод;
-
перемещения по файлу;
-
специальные операции.
Установочные и завершающие операции
Так как имя файловой переменной назначается произвольно по правилам языка, то, чтобы связать его с конкретным физическим файлом на внешнем носителе информации, используется процедура
Assign (<имя_файловой_переменной>,<строка>);
Здесь имя задается в разделе описания переменных, а строкой является внешнее имя файла с возможным указанием пути, например:
Assign (Fil, 'c:\document\myfile.txt');
Строка может быть и переменной, например, можно вводить имя файла с клавиатуры. В качестве строки могут выступать и имена внешних устройств, принятые в MS-DOS: PRN, CON и другие.
В общем случае над файлами можно производить только две операции – просмотр файла и создание файла, все остальные являются их производными. Поэтому существуют две различные процедуры
Reset (< имя_файловой_переменной >);
Rewrite (< имя_файловой_переменной >);
Первая предназначена для открытия файла для просмотра. То есть на внешнем устройстве ищется файл, образуется специальный системный буфер для обмена, буферная переменная устанавливается на начало файла, то есть на его нулевой элемент. При этом предполагается, что открываемый файл уже существует, в противном случае возникает ошибка.
Вторая выполняет аналогичные действия, но создает не существовавший ранее файл. Если же такой файл уже существует, то при выполнении этой процедуры файл без предупреждения очищается.
После завершения работы с файлом его необходимо закрыть, то есть выполнить операции, обратные выполняемым двумя предыдущими процедурами. Для этого предназначена процедура
Close (< имя_файловой_переменной >);
Хотя при завершении работы всей программы происходит автоматическое закрытие всех файлов, но использование этой процедуры является правилом хорошего тона. Более того, в некоторых случаях без нее не обойтись, например, при создании файла, а затем использовании из него данных.
Операции ввода-вывода
Это две процедуры, которые и реализуют действия по чтению информации из файла и записи ее в файл.
Read (<имя_файловой_переменной >,<список_ввода>);
Write (<имя_файловой_переменной >,<список_вывода>);
В отличие от других процедур, они могут вызываться с различным числом параметров, и эти параметры могут иметь различные типы (объектно-ориентированные процедуры).
Выполнение процедуры Read происходит, начиная с текущей позиции указателя файла. При этом последовательно читаются значения, содержащиеся в файле, и присваиваются очередной переменной из списка ввода. После каждого чтения указатель файла смещается на следующую позицию. Если указатель файла будет установлен на позицию, не содержащую информации, то есть будет достигнут конец файла, то возникает ситуация «конец файла», обнаруживаемая с помощью специальной функции EOF.
Так как файл может быть пустым, то есть не содержать ни одного компонента, то при циклическом чтении информации из файла необходимо использовать оператор цикла While.
Процедура Write имеет обратный смысл, позволяя записывать в файл информацию из программы. Первым параметром должна быть файловая переменная, открытая процедурой Rewrite. Далее должен идти список выражений, тип которых совпадает с базовым типом файла.
Перемещения по файлу
Это группа дополнительных процедур и функций, позволяющая изменять последовательный порядок операций чтения и записи. Единственным исключением является базовая логическая функция EOF, указывающая на конец файла:
EOF (< имя_файловой_переменной >)
При чтении, если все данные прочитаны, возникает ситуация «конец файла», и эта функция принимает значение True, иначе она равна False. Так как при записи данные всегда добавляются в конец файла, то функция имеет постоянное значение True.
Процедура
SEEK (< имя_файловой_переменной >,<выражение>);
позволяет явно изменять значение указателя файла, устанавливая его на компонент файла с номером, заданным выражением. Здесь выражение должно быть целого типа Longint.
Эту процедуру можно применять не только для произвольного чтения элементов файла, но и для его усечения. Если затем использовать процедуру
TRUNCATE (< имя_файловой_переменной >);
то компоненты файла, следующие за текущем указателем включительно, будут удалены.
При использовании процедуры Seek может возникнуть ситуация, когда компонентов в файле меньше, чем указанное значение, и может возникнуть аварийная ситуация. Чтобы этого избежать, используют функции
FileSize (< имя_файловой_переменной >)
FilePos (< имя_файловой_переменной >)
Они позволяют получить дополнительную информацию о файле и возвращают: первая – общее число элементов в файле, вторая – номер элемента, на который установлен указатель файла.
Пример работы с файлом
В качестве примера приведем статистическую обработку информации, находящейся в файле из текущего раздела, имя которого вводится с клавиатуры, а расширение .DAT назначено по умолчанию. В программе вычисляются математическое ожидание и дисперсия вещественных чисел соответственно по формулам:
и .
Program Statistic;
Var
Fil:File of real; { входной файл }
x, { очередное вводимое число }
M,D: real; { матожидание и дисперсия }
N: integer; { количество введенных значений }
Name: string; { вводимое имя файла (без расширения) }
Begin
N:=0; M:=0; D:=0;
Read(Name); { вводим имя файла }
{ ставим в соответствие внутреннее и внешнее имена }
Assign(Fil,Name+'.dat');
Reset(Fil); { открываем файл для чтения}
While not Eof(Fil) do Begin
N:=N+1;
Read(Fil,x); { в цикле вводим все значения }
M:=M+x; { и суммируем их }
D:=D+x*x;
end;
M:=M/N;
D:=sqrt(D/N-M*M);
Writeln(' Матожидание= ',M:10:5,
' Дисперсия= ', D:10:5);
end.
При попытке открыть несуществующий файл может возникнуть аварийная ситуация, поэтому необходимо проверять корректность операций.
Обработка ошибок ввода-вывода
При выполнении программы, написанной на языке Турбо Паскаль, установлены следующие правила обработки ошибочных ситуаций, связанных с вводом и выводом. По умолчанию при выполнении любой такой операции автоматически производится проверка на возникновение ошибки. При обнаружении ошибки выполнение программы прекращается, на экран выводится диагностическое сообщение с условным номером ошибки.
Можно предусмотреть в самой программе реакцию на ошибочные ситуации. Для этого используется директива компилятора {$I-}. В этом случае возникновение ошибки не будет приводить к немедленному завершению программы, а код этой ошибки будет запомнен в качестве значения стандартной функции IOResult без параметров. При отсутствии ошибок значение этой функции равно нулю. Например:
...
Writeln (' Введите имя файла');
Read (NameFil);
Assign (Fil,NameFil);
{$I-} { автоматический контроль отключен }
Reset (Fil);
Code:=IOResult;
If Code <> 0 Then Begin
Write ('Ошибка при открытии файла ',
NameFil,': ');
Case Code of
2: Writeln ('файл не найден');
3: Writeln ('путь к файлу не найден');
4: Writeln ('слишком много открытых файлов');
5: Writeln ('доступ закрыт');
6: Writeln ('нарушена информация в полях файла или системных областях');
8: Writeln ('недостаточно памяти');
10: Writeln ('несовместимые параметры окружения');
11: Writeln ('нераспознанный формат диска');
else
Writeln ('нераспознана');
end;
Exit;
end;
{$I+} { автоматический контроль включен }
...
При использовании функции IOResult, если отключен режим автоматического контроля, то после возникновения ошибки все последующие операции с любым файлом будут игнорироваться, пока не произойдет обращение к функции IOResult. Поэтому рекомендуется вызывать эту функцию сразу после выполнения операции, связанной с файлом. Кроме этого, обращение к функции обнуляет код ошибки, поэтому повторное обращение будет давать нулевой результат.
Специальные операции
Эта группа операций предназначена для действий с элементами файловой системы ОС – каталогами и файлами. К ней относятся следующие процедуры:
Erase (< имя_файловой_переменной >);
Rename (< имя_файловой_переменной >, <строка>);
-
установка текущего каталога
ChDir;
MkDir (<имя_подкаталога>);
-
удаление пустого подкаталога
RmDir (<имя_подкаталога>);
Перечисленные средства работы с файлами являются стандартными, при использовании модуля DOS возможно применение дополнительных процедур и функций.
Пример объединения двух файлов
Из двух отсортированных в убывающем порядке файлов целых чисел IN1.DAT и IN2.DAT получается новый файл OUT.DAT, отсортированный в том же порядке.
Program Merge;
{ Учебный пример:
Создание файла OUT.DAT, отсортированного в убывающем порядке, объединенного из файлов IN1.DAT и IN2.DAT, так же отсортированных }
Var In1,In2,Out: File of integer;
x1,x2: integer; { вспомогательные переменные - элементы файлов }
Begin
{ Ставим в соответствие внутреннее и внешнее имя файлов }
Assign(In1,'IN1.DAT');
Assign(In2,'IN2.DAT');
Assign(Out,'OUT.DAT');
{$I-} { Запрет контроля ошибок в среде ОС }
{ Открываем и контролируем открытие файлов }
Reset(In1);
If IOResult <>0 Then Begin
Writeln ('Ошибка файла IN1.DAT');
Exit; End;
Reset(In2);
If IOResult <>0 Then Begin
Writeln ('Ошибка файла IN2.DAT');
Exit; End;
Rewrite(Out);
If IOResult <>0 Then Begin
Writeln ('Ошибка создания файла OUT.DAT');
Exit; End;
{$I+} { Обязательно восстанавливаем контроль за ошибками }
{ в цикле While обрабатываются элементы файлов }
While not (EOF(In1) or EOF(In2)) do
Begin
{ Читаем элемент файла, но указатель не перемещаем, иначе будет пропущен последний элемент }
Read(In1,x1); Seek(In1,FilePos(In1)-1);
Read(In2,x2); Seek(In2,FilePos(In2)-1);
If x1
Write(Out,x2);
Seek(In2,FilePos(In2)+1)
end
else Begin
Write(Out,x1);
Seek(In1,FilePos(In1)+1)
end;
end;
{ Чтение одного из входных файлов закончено – переписываем конец незаконченного файла в выходной }
While not EOF(In1) do
Begin
Read(In1,x1);
Write(Out,x1);
end;
While not EOF(In2) do
Begin
Read(In2,x2);
Write(Out,x2);
end;
{ Закрытие использованных файлов – хороший стиль программирования}
Close (In1);
Close (In2);
Close (Out);
end.
Текстовые файлы
На персональных компьютерах значительное количество информации обрабатывается в виде текстов, хранящихся в текстовых файлах. Их структура отличается от структуры обычных файлов тем, что содержимое текстового файла рассматривается как последовательность символьных строк переменной длины, разделенных комбинацией символов, называемой «конец строки». Эти файлы завершаются специальным кодом «конец файла».
Знание конкретной кодировки управляющих символов не обязательно, так как они автоматически учитываются при выполнении операций над текстовыми файлами, но это и обеспечивает специфику данного вида файлов.
Файлы, имеющие такую структуру, имеют стандартный тип:
Type <имя_типа> = text;
Var < имя_файловой_переменной >: text;
и имеют в своем составе элементы литерного типа, которые включают управляющие символы.
Набор операций, применимых к текстовым файлам, содержит, кроме операций для обычных файлов, следующие. К начальным операциям добавлена процедура
Append (< имя_файловой_переменной >);
предназначенная для записи информации в файл. Ее действие аналогично Rewrite, но при наличии файла она не очищает файл, а ставит указатель файла на его конец. Таким образом, процедура Append используется, когда необходимо добавить новые строки в конец уже существующего файла.
Так же используется процедура SetTextBuf, определяющая буфер для обмена с текстовым файлом, здесь не рассматривается.
В операциях, связанных с вводом-выводом, помимо процедур Read и Write, имеются две их модификации:
ReadLn (< имя_файловой_переменной >[,<список_ввода>]);
WriteLn (< имя_файловой_переменной >[,<список_ввода>]);
Они выполняют аналогичные действия, но после операций чтения или записи производят переход к следующей строке текстового файла. Хотя операция Read и переходит автоматически к следующей строке после окончания текущей, но процедура ReadLn выполняет это принудительно, не дожидаясь окончания строки, то есть возможен пропуск данных.
При посимвольной обработке, чтобы проверить, достигнут ли конец текущей строки, используется функция
EOLn (< имя_файловой_переменной>).
Таким образом, типовая схема обработки текстового файла включает в себя двойной цикл. При чтении файла:
...
Assign (< имя_файловой_переменной >,
<внешнее_имя_файла >);
Reset (< имя_файловой_переменной >);
{ Возможна обработка особых ситуаций }
While not EOF (<имя_файловой_переменной >) do
Begin
While not EOLn (<имя_файловой_переменной >) do
Begin
Read (< имя_файловой_переменной >,
< символьная переменная >);
< операторы обработки символа >;
end;
ReadLn (<имя_файловой_переменной >);
end;
Close (<имя_файловой_переменной >);
...
При создании файла:
...
Assign (<имя_файловой_переменной >,
<внешнее_имя_файла >);
Rewrite (<имя_файловой_переменной >);
{ Возможна обработка особых ситуаций }
While < признак_окончания_файла > do
Begin
While <признак_окончания_строки> do
Begin
Write (<имя_файловой_переменной >,
< символьная переменная >);
< операторы формирования символа >;
end;
WriteLn (<имя_файловой_переменной >);
end;
Close (<имя_файловой_переменной >);
...
Стандартные текстовые файлы
В любой программе считаются уже описанными и открытыми два текстовых файла, предназначенные для обмена со стандартными устройствами ввода-вывода. При вводе это файл Input, связанный с клавиатурой, при выводе – Output, – монитор.
Эти файлы рассматриваются как параметры по умолчанию в операциях работы с текстовыми файлами, когда файл явно не указан. Таким образом:
Write (CH) соответствует Write (Output,CH);
Read (CH) соответствует Read (Input,CH);
WriteLn соответствует WriteLn (Output);
ReadLn соответствует ReadLn (Input);
EOF соответствует EOF (Input);
EOLn соответствует EOLn (Input).
В соответствии с общими правилами MS-DOS стандартные файлы могут быть переназначены, то есть связаны с другими устройствами или дисковыми файлами. Для этой цели можно использовать процедуру Assign, например:
Assign (Output, ‘myfile.out’);
После этого все операции вывода, неявно использующие этот файл, будут выводить информацию на диск в текущий каталог и файл myfile.out.
При вводе и выводе числовых данных (как и других констант простых типов) в текстовый файл они представляются в литерном виде и разделяются либо произвольным количеством пробелов, либо признаком окончания строки. Перевод чисел из машинного представления в литерное и наоборот осуществляется автоматически. При чтении только числовых данных можно не анализировать конец строки, процедура Read сама осуществляет переход к другой строке. Например, вводятся исходные данные
3.51 -16 15 14
5.6 -10.2Е-3
для фрагмента программы
Var i,j,k: Integer;
r,s,t: Real;
...
Begin
Read (r,i,j,k,s,t);
...
Более сложная организация ввода потребуется, если исходные данные содержат смесь литерных и числовых данных. Например, вводится календарная дата, состоящая из дня (целое), месяца (три символа) и года (целое). Между этими тремя данными может быть произвольное количество пробелов, а может и не быть ни одного. Хотя после первого числа обязательно должен быть пробел, — как разделитель, иначе произойдет ошибка ввода.
Program Input_Date;
Var i,j: integer;
ch: char;
mch: array [1..3] of char;
Begin
Read(i);
Repeat
Read(ch);
Until ch <> ' ';
mch[1]:=ch;
Read (mch[2]);
Read (mch[3]);
Read (j);
{ Writeln (i,' ',mch,' ',j); }
end.
В данном случае нельзя целиком ввести массив mch, так как количество пробелов неопределенно.
Файлы без типа
Этот тип данных используется только в Турбо-Паскале и описывается следующим образом:
Var <имя>: File;
Понятие нетипизированных файлов используется для организации доступа к любым дисковым файлам независимо от их структуры. Файл представляется как последовательность компонентов произвольного типа, но необходимо определить размер этих компонентов.
Открываются эти файлы теми же процедурами, что и обычные, но вторым параметром должен быть задан размер компонента в байтах, например:
Assign (Fil,’Data.dat’);
Reset (Fil,512);
Если параметр отсутствует, то по умолчанию размер предполагается равным 128 байт. Для обеспечения максимальной скорости обмена размер компонента рекомендуется выбирать кратным размеру физического сектора диска, например 512 байт. С другой стороны, размер файла может быть не кратен выбранному размеру, поэтому, чтобы гарантированно обеспечить полное чтение всего файла, нужно использовать размер компонента 1.
Для организации обмена с нетипизированными файлами используются специальные процедуры, здесь не рассматриваемые.
-
Что такое массив?
-
Как описывается массив?
-
Что такое размер и размерность массива?
-
Чем отличается краткая от полной формы записи индексов массива?
-
Каково максимальное количество памяти, которое может занимать один массив в ОС MS DOS?
-
Как описываются строки определенной длины?
-
Какова максимальная длина строки?
-
Как наиболее просто обратиться к отдельному символу в строке?
-
Что такое «конкатенация»?
-
С помощью какой операции можно объединить две или более строки?
-
Какую функцию используют для сцепления строк, какова форма ее записи?
-
Какую функцию используют для определения длины строки, какова форма ее записи?
-
Какую функцию используют для копирования фрагмента строки, какова форма ее записи?
-
Какую функцию используют для определения номера символа в строке, с которого начинается определенная подстрока, какова форма ее записи?
-
Какую процедуру используют для вставки в исходную строку другой подстроки, какова форма ее записи?
-
Какую процедуру используют для удаления из строки ее фрагмента, какова форма ее записи?
-
Какую процедуру используют для преобразования строки символов в целое или вещественное значение, какова форма ее записи?
-
Что такое «запись»?
-
Как описывается тип «запись»?
-
Как обращаться к элементам записи?
-
Что такое «сочленяющая точка»?
-
Из каких двух частей может состоять запись?
-
Для какой цели используется оператор With?
-
Что такое «множество»?
-
Как описывается тип «множество»?
-
Какие простые типы в Турбо-Паскале можно использовать для создания множеств?
-
Как называются константы для множеств?
-
Как обозначается пустое множество?
-
Что произойдет, если при задании в конструкторе множества диапазона, первый элемент будет больше второго?
-
Что такое «объединение множеств»?
-
Что такое «пересечение множеств»?
-
Что такое «разность множеств»?
-
Что такое «включение множеств»?
-
Как обозначается и какой дает результат операция проверки принадлежности множеству?
-
Что в Паскале подразумевается пол файлом?
-
Что такое текущий указатель файла?
-
Почему в общем случае нельзя использовать имена файлов в программе такие же, как и задаваемые в операционной системе?
-
На какие 4 группы можно условно разбить все операции над файлами?
-
Как записывается процедура, ставящая в соответствие имя файловой переменной и конкретный файл?
-
Как записывается процедура, открывающая файл для просмотра?
-
Какие действия выполняются при открытии файла?
-
Как записывается процедура, открывающая файл для записи?
-
Как записывается процедура, завершающая работу с файлом?
-
Как записывается процедура, выполняющая чтение данных из файла?
-
Как записывается процедура, выполняющая запись данных в файл?
-
Как записывается и как используется функция, указывающая на конец файла?
-
Какой процедурой можно явно изменить значение указателя файла?
-
С помощью какой процедуры можно удалить все элементы файла, следующие за указателем файла?
-
С помощью какой функции можно определить количество элементов в файле?
-
С помощью какой функции можно определить значение файловой переменной?
-
С помощью какой функции можно определить результат выполнения операции ввода-вывода в файл?
-
Какое значение принимает функция IOResult при корректном выполнении операции ввода-вывода?
-
Каковы особенности текстового файла?
-
Как описываются текстовые файлы?
-
С помощью какой процедуры можно добавлять строки в конец файла?
-
С помощью какой процедуры можно читать из файла текст с начала строки?
-
С помощью какой процедуры можно записывать в файл текст с начала строки?
-
Как проверить, достигнут ли в файле конец строки?
-
Каково имя файла для стандартного устройства ввода?
-
Каково имя файла для стандартного устройства вывода?
-
Как разделяются числа в текстовом файле?
-
Зачем используются и как описываются файлы без типа?
Достарыңызбен бөлісу: |