Глава 11. ОРГАНИЗАЦИЯ
ВВОДА-ВЫВОДА
В языке Паскаль ввод-вывод информации осуществляется через файловые переменные.
Перед тем, как осуществлять ввод-вывод, файловая переменная должна быть связана с конкретным
внешним файлом с помощью процедуры Assign. Этим файлом может быть файл на магнитном диске
или ленте, а также устройство вычислительного комплекса (клавиатура, экран дисплея, принтер
и т.д.). Затем файл должен быть открыт для чтения и/или записи. После этого можно
осуществлять организацию ввода-вывода.
В Turbo Pascal имеется 3 категории файлов: текстовые (тип Text), типизированные
(file of <тип>) и файлы без типа (file) (см. п. 6.6). Работа с этими категориями файлов
несколько отличается друг от друга.
Обычно все файлы считаются файлами последовательного доступа. Однако для типизированных
файлов и файлов без типа с помощью процедуры Seek можно установить режим произвольного
доступа. При этом нумерация компонент файла начинается с нуля.
После работы с файлом он, как правило, должен быть закрыт процедурой Close. Это требование
обязательно должно соблюдаться для файла, в который производилась запись.
При работе с файлами автоматически проверяются ошибки ввода-вывода. Если проверка ошибки
включена (по умолчанию или с помощью ключа компилятора {$I+}), то при возникновении ошибки
выполнение программы автоматически завершается, если же проверка ошибки отключена (с помощью
ключа {$I-}), программа продолжает выполняться и можно проверить результат ввода-вывода с
помощью функции IOResult.
11.1. Стандартные процедуры и функции для всех файлов
В данном разделе будут кратко описаны процедуры и функции, которые применимы для всех файлов.
Во всех этих процедурах и функциях F - файловая переменная, связанная с конкретным физическим
файлом процедурой Assign.
ПРОЦЕДУРЫ
Assign (F, Name) - связь файловой переменной с внешним файлом. Связывает файловую переменную
F с внешним файлом, имеющим имя Name. Name - переменная или константа типа string (или
совместимого для присваивания с ним типа) или типа PChar. Имя типа должно быть написано в
соответствии с правилами MS DOS, может включать путь и не должно превышать 79 символов. Если
строка имени пустая, осуществляется связь со стандартным файлом ввода или вывода (как
правило, консолью).
ChDir(Path) - изменение текущей директории.
Устанавливает новый текущий каталог, путь к которому (включая, быть может, и имя устройства)
находится в параметре Path. Параметр Path по своим характеристикам аналогичен параметру Name
в процедуре Assign, но в него не должно входить имя файла и он может быть только типа string
(или совместимого для присваивания с ним типа).
Close(F) - закрытие открытого файла.
Закрывает внешний файл, с которым связана файловая переменная F. При этом в случае
необходимости в содержимое файла вносятся все произведенные изменения.
Erase (F) - уничтожение внешнего файла. Удаляется внешний файл, с которым связана файловая
переменная F.
GetDir(N,S) - получение текущего каталога накопителя. В переменную S типа string записывается
текущий каталог накопителя, номер которого указан параметром N. Параметр N имеет следующие
значения: 0 - текущий накопитель, 1 - накопитель А:, 2 - накопитель В: и т. д.
MkDir(Path) - создание каталога.
Создается новый каталог, путь до которого указан параметром Path. Параметр Path по своим
характеристикам аналогичен параметру Name в процедуре Assign, но в него не должно входить имя
файла, и он может быть только типа string (или совместимого для присваивания с ним типа).
Rename (F,NewName) - переименование внешнего файла. Внешний файл, с которым связана файловая
переменная F, получает новое имя, заданное параметром NewName. Параметр NewName по своим
характери¬стикам аналогичен параметру Name в процедуре Assign.
Reset(F[,Size]) - открытие существующего файла.
Открывается существующий файл, с которым связана файловая переменная F, и указатель текущей
компоненты файла настраивается на начало файла. Необязательный параметр целого типа Size
используется только с файлами без типа и задает размер пересылаемого элемента информации в
байтах. По умолчанию этот размер равен 128.
Rewrite (F[,Size]) - открытие нового файла.
Открывается новый пустой файл, и ему присваивается имя, заданное процедурой Assign. Если файл
с таким именем уже существует, то он уничтожается. Необязательный параметр Size имеет тот же
смысл, что и в процедуре Reset.
RmDir(Path) - удаление пустого каталога.
Удаляется пустой каталог, путь до которого указан параметром Path. Параметр Path по своим
характеристикам аналогичен параметру Name в процедуре Assign, но в него не должно входить имя
файла, и он может быть только типа string (или совместимого для присваивания с ним типа).
ФУНКЦИИ
Eof (F) - конец файла.
Принимает значение True, если указатель текущей компоненты файла находится за последней
компонентой файла (за последним символом, если файл текстовый), и False - в противном случае.
IOResult- результат последней операции ввода-вывода.
Возвращает число 0, если операция ввода-вывода завершилась успешно, и другое число - в
противном случае. После применения этой функции параметр состояния последней операции
ввода-вывода сбрасывается в 0.
11.2. Стандартные процедуры и функции для текстовых файлов
Текстовый файл представляет собой совокупность символов, разделенных на строки, причем в
конце каждой строки стоит признак конца строки.
Особенностью работы с текстовыми файлами является то, что параметры, значения которых
вводятся и выводятся с помощью процедур Read или Write, могут быть не обязательно типа Char
или string, а и других простых типов (целых, вещественных типов при вводе; целых,
вещественных типов, типа Boolean - при выводе). Эти процедуры могут также работать и с
ASCHZ-строками.
Имеется две стандартные файловые переменные для текстового файла: Input и Output. Переменная
Input no умолчанию связана с клавиатурой, a Output - с экраном дисплея. Эти устройства
автоматически открываются при запуске программы, т. е. фактически выполняются процедуры:
Assign(Input,''); Reset(Input);
Assign(0utput,''); Rewrite(Output);
Они также автоматически закрываются после работы программы.
Если при работе с файлами типа Text в процедуре или функции не указана файловая переменная,
считается, что используется файловая переменная Input или Output.
Файл типа Text может быть открыт либо для чтения процедурой Reset, либо
для записи процедурой Rewrite или Append
Далее кратко описаны процедуры и функции, используемые для файлов типа Text. Так же как и
ранее, в этих подпрограммах F - файловая переменная, связанная с конкретным физическим файлом
процедурой Assign.
ПРОЦЕДУРЫ
Append (F) - открытие файла для добавления в конец информации. Открывается существующий файл,
с которым связана файловая переменная F, и указатель текущей компоненты файла настраивается
на конец файла.
Flush (F) - освобождение буфера выходного файла.
Информация из буфера вывода записывается в файл, с которым связана файловая переменная F.
Read(F, <список ввода>) - чтение информации из файла. Из файла, с которым связана файловая
переменная F, читаются значения для одной или нескольких переменных списка ввода (см. ниже).
ReadLn(F, <список ввода>) - чтение строки из файла.
То же, что и процедура Read, но непрочитанная часть строки, включая признак конца строки,
пропускается.
SetTextBuf(F, Buf, Size]) - назначение буфера ввода-вывода. Для файла, с которым связана
файловая переменная F, назначается буфер ввода-вывода в виде переменной Buf (любого типа).
Необязательный параметр Size задает размер буфера в байтах. Если этот параметр не указан, в
качестве буфера используется вся переменная Buf. Если эта процедура не используется, для
организации ввода-вывода берется стандартный буфер ввода-вывода размером в 128 байтов.
Write(F, <список вывода>) - запись информации в файл.
В файл, с которым связана файловая переменная F, записываются значения выражений списка
вывода (см. ниже).
WriteLn(F, <список вывода>) - запись строки в файл.
То же, что и процедура Write, но выводимая информация завершается признаком конца строки.
ФУНКЦИИ
Eoln (F) - конец строки файла.
Принимает значение True, если текущей компонентой файла является признак конца строки или
если функция Eof (F) принимает значение True. В остальных случаях функция принимает значение
False.
SeekEof (F) - конец файла.
Отличается от Eof (F) тем, что стоящие в конце файла символы пробела и табуляции пропускаются.
SeekEoln(F) - конец строки файла.
Отличается от Eoln (F) тем, что стоящие в конце строки символы пробела и табуляции
пропускаются.
Пример. Прочитать из текстового файла А все записанные в него целые числа, преобразовать их в
вещественные и вывести в текстовый файл В по 4 числа в строку.
program EXAMPLE16;
var Fl, F2: Text;
X: Real;
i: Integer;
begin
{$I-} {отключается внутренняя проверка правильности операций с файлом}
Assign(Fl, 'А'); {связь с файлом А, содержащим только целые числа}
Reset(Fl); {открыть файл для чтения}
{$I+}
if IOResult <>0 then
Writeln('Нет файла А') else
begin
Assign(F2, 'В'); {связь с файлом В}
Rewrite(F2); {открыть файл для записи}
repeat
for i := 1 to 4 do
if not SeekEof(Fl) then
begin
Read(Fl, X);
Write(F2, X: 18) {Форматный вывод - см. ниже}
end;
WriteLn(F2); {переход на новую строку}
until SeekEof(Fl);
Close(Fl); Close(F2)
end
end.
Ввод-вывод информации при работе с текстовым файлом (особенно когда в качестве файла
выступает клавиатура или экран дисплея) осуществляется настолько часто, что стоит об этом
сказать подробнее.
Ввод информации из файла осуществляется с помощью процедур Read и ReadLn. Форма использования
этих процедур следующая:
Read(F, <список ввода>);
ReadLn(F, <список ввода>);
В этих операторах F - файловая переменная, связанная с файлом, из которого вводится
информация. Как указывалось выше, если эта файловая переменная отсутствует, считается, что
используется стандартная файловая переменная Input, a ввод стандартно осуществляется с
клавиатуры. Список ввода представляет собой перечисленные через запятую имена переменных,
которым присваиваются введенные значения. Переменные могут быть целого, вещественного,
символьного типа, типа string или типа PChar. Их количество может быть любым (в том числе
и пустым). Так как в текстовом файле информация хранится в виде символов, то при вводе
значений целого и вещественного типа автоматически происходит перекодирование информации в
двоичный формат. Отдельные числа в текстовом файле должны отделяться пробелами, знаками
табуляции или признаками конца строки. При вводе символа из файла читается один символ, в том
числе и символ возврата каретки (код 13), если функция Eoln(F) дает значение True, или символ
конца файла (код 26), если функция Eof(F) дает значение True. При вводе величины типа string
или PChar из файла читается необходимое число символов, но в пределах одной строки (до
символа конца строки - код 13). При этом, если чтение осуществляется процедурой Read, признак
конца строки не читается и при последующем чтении строковой информации в соответствующие
переменные ничего заноситься не будет до тех пор, пока не будет прочитан признак конца строки
(например, процедурой Readln).
Пример.
var А, В: Integer; С: Real; D, E: Char;
ReadLn(A, В, С, D, E);
В этом примере с клавиатуры вводятся два целых числа, одно вещественное число и два символа.
Вывод информации в текстовый файл (в том числе и на экран дисплея) осуществляется с помощью
процедур Write и WriteLn.
Write(F, <список вывода>);
WriteLn(F, <список вывода>);
В этих операторах, как и в операторах ввода, F - файловая переменная. Список же вывода - это
перечисленные через запятую выражения, значения которых выводятся в текстовый файл. Выражения
могут быть целого, вещественного, логического и символьного типа, а также типа string или
PChar. Число элементов списка вывода может быть любым (в том числе и нулевым; при этом
процедура Write не выполняет никаких действий, а процедура WriteLn осуществляет переход на
новую строку). При выводе чисел происходит их автоматическая перекодировка в символьную форму.
В случае логической величины выводится ее зна¬чение словом TRUE или FALSE.
Так как элементы списка вывода в общем случае являются выражениями, то они, в частности,
могут быть и строковыми константами. Это позволяет осуществлять комментированный вывод
информации, например:
WriteLn('Площадь фигуры равна: ', SFig);
WriteLn('Строка ', Name, ' содержит ', Len, ' символов');
В Turbo Pascal при выводе информации в текстовый файл по умолчанию отводится количество
позиций, равное размеру этой информации. Так, например, при выводе символа отводится одна
позиция, при выводе строки - число позиций, равное длине строки, при выводе целого числа -
число позиций, равное числу значащих цифр плюс одна позиция под знак, если число
отрицательное.
Это необходимо учитывать при выводе последовательных элементов информации (например,
нескольких целых чисел) и предусматривать меры, чтобы отдельные элементы информации не
сливались друг с другом: вывод разделяющих пробелов, разнесение информации по разным строкам
и т. д. Исключение составляют вещественные числа - под них выделяется 17 позиций, и при
выводе они выравниваются по правому краю выделенного поля.
Изменить стандартную форму вывода можно используя систему форматов языка Паскаль. Всего в
языке существует два формата вывода, которые записываются через двоеточие после
соответствующего элемента вывода, например:
Write(Expression: M: N);
В этом примере Expression - выражение, значение которого выводится в файл (в данном случае -
на экран дисплея); М и N - форматы.
Форматы М и N представляют собой в общем случае выражения целого типа. Первый формат можно
использовать при выводе значений любого типа, и он определяет размер поля, в которое
выводится данное значение (см. выше программу EXAMPLE16). Выводимая информация выравнивается
по правому краю поля. Если для вывода конкретной информации выделенного поля недостаточно,
оно автоматически увеличивается до нужного размера. Если при выводе вещественного числа этот
формат задан меньше 8, то он автоматически увеличивается до 8. Второй формат используется
только с вещественными числами и определяет число знаков после десятичной точки, которые
необходимо отобразить в файле. Если этот формат используется, то вещественное число
представляется в форме с фиксированной точкой, в противном случае - с плавающей точкой.
Пример. Вывести на экран дисплея таблицу умножения целых чисел от 1 до 9.
program EXAMPLE17;
var i, j: Byte;
begin
WriteLn;
WriteLn('ТАБЛИЦА УМНОЖЕНИЯ': 45);
WriteLn;
Write( ' ':10);
for j := 1 to 9 do
Write(j: 6);
WriteLn; WriteLn;
for i := 1 to 9 do
begin
Write(i:6, ' ': 4);
for j := 1 to 9 do
Write(i * j: 6);
WriteLn
end
end.
11.3. Стандартные процедуры и функции для типизированных файлов
При работе с типизированными файлами используются следующие дополнительные процедуры и
функции (здесь также F - файловая переменная, связанная с конкретным физическим файлом
процедурой Assign).
ПРОЦЕДУРЫ
Read(F, <список ввода>) - чтение информации из файла.
То же, что и процедура Read для текстовых файлов, но переменные, в которые читается
информация, должны быть того же типа, что и компоненты файла.
Seek(F, Num) - настройка на требуемую компоненту файла. Осуществляется настройка на
компоненту файла, с которым связана файловая переменная F. Компонента файла определяется
номером Num, причем нумерация компонент начинается с нуля.
Truncate (F) - удаление части файла, начиная с текущей позиции. Удаляется часть файла,
начиная с текущей позиции и до его конца.
Write (F, <список вывода>) - запись информации в файл.
То же, что и процедура Write для текстовых файлов, но список вывода представляет собой
переменные того же типа, что и компоненты файла.
ФУНКЦИИ
FilePos(F) - номер текущей компоненты файла.
Функция возвращает номер текущей компоненты файла, с которым связана файловая переменная F.
Нумерация компонент начинается с нуля.
FileSize (F) - текущий размер файла.
Функция возвращает текущий размер файла, с которым связана файловая переменная F, в
компонентах этого файла.
Пример. Из текстового файла Т прочитать попарно вещественные числа и,
считая в каждой паре первое число действительной, а второе - мнимой составляющей, записать
их в файл комплексных чисел С.
program EXAMPLE18;
type
Complex = record
Re, Im: Real;
end;
var F1: Text;
F2: file of Complex;
X : Complex;
begin
{$I-}
Assign(Fl, 'T');
Reset(Fl);
{$I+}
if IOResult <> 0 then
Writeln('Heт исходного файла') else
begin
Assign(F2, 'С');
Rewrite(F2) ;
while not SeekEof(Fl) do
begin
Read(Fl, X.Re);
if SeekEof(Fl) then
X.Im:=0 else
Read(Fl, X.Im);
Write(F2, X)
end;
Close(Fl); Close(F2)
end
end.
11.4. Стандартные процедуры и функции для файлов без типа
При работе с файлами без типа используются следующие дополнительные процедуры и функции
(о переменной F см. выше).
ПРОЦЕДУРЫ
BlockRead(F, Buf, N [ ,Result]) - чтение из файла компонент. Из файла, с которым связана
файловая переменная F, читаются N или менее (если при чтении будет достигнут конец файла)
компонент и помещаются в буфер. В качестве буфера используется переменная Buf подходящего
размера. Необязательный параметр Result возвращает фактическое количество прочитанных
компонент. Максимальный размер переносимой информации равен N * Size (см. процедуры Reset и
Rewrite) и не должен превышать 65535 байт. После выполнения процедуры указатель текущей
компоненты файла перемещается на соответствующее количество прочитанных компонент.
BlockWrite(F, Buf, N [, Result]) - запись в файл компонент. В файл, с которым связана
файловая переменная F, записываются N или менее (если в процессе записи будет до конца
заполнен диск) компонент из буфера Buf. Необязательный параметр Result возвращает фактическое
количество записанных компонент. Максимальный размер переносимой информации равен N * Size
(см. процедуры Reset и Rewrite, где описан параметр Size) и не должен превышать 65535 байт.
После выполнения процедуры указатель текущей компоненты файла перемещается на соответствующее
количество записанных компонент.
Seek(F, Num) - настройка на требуемую компоненту файла. Аналогична процедуре Seek для
типизированных файлов.
Truncate (F) - удаление части файла, начиная с текущей позиции. Аналогична процедуре Truncate
для типизированных файлов.
ФУНКЦИИ
FilePos(F) - получение номера текущей компоненты файла.
Аналогична функции FilePos для типизированных файлов.
FileSize(F) - получение текущего размера файла.
Аналогична функции FileSize для типизированных файлов.
Как указывалось выше, при работе с файлами без типа в процедурах Reset и Rewrite можно
использовать дополнительный параметр, определяющий размер одной компоненты записи в байтах.
Пример. Из текстовою файла Т прочитать находящиеся там символы, заменить их на символы,
отличающиеся своими кодами от исходных на определенную величину, меняющуюся от символа к
символу (шифрация методом простой одноалфавитной подстановки). Поместить эти символы в новый
файл, разместив в нем предварительно число перекодированных символов и таблицу смещений кодов.
program EXAMPLE19;
const
NOfCod = 20; {Размер таблицы смещений кодов}
var FirstFile: Text; {Исходный файл}
SecondFile: file; {Результирующий файл}
FirstName: string;
SecondName: string;
IORes: Byte; (Код результата работы с файлом)
NOfSymb: Longint; {Число символов в файле}
Codes: array[1..NOfCod] of Byte; {Таблица смещений кодов символов}
Buffer: array[1..NOfCod] of Char; {Буфер для символов}
i: Word;
{ Процедура записи в файл с проверкой}
procedure WriteAndControl(var Buf; Amount: Word);
var Result: Word; {Число переданных символов}
begin
BlockWrite(SecondFile, Buf, Amount, Result);
if Result <> Amount then
begin
WriteLn('Heт места на диске');
Halt
end
end;
begin
{ Связь с исходным тектовым файлом для чтения }
repeat
{$I-}
Write('Имя исходного файла: ');
ReadLn(FirstName) ;
Assign(FirstFile, FirstName);
Reset(FirstFile);
{$I+}
IORes := IOResult;
if IORes <> 0 then
WriteLn('Такого файла нет')
until IORes = 0;
{ Связь с результирующим файлом без типа для записи }
repeat
{$I-}
Write('Имя результирующего файла: ');
ReadLn(SecondName);
Assign(SecondFile, SecondName);
Rewrite(SecondFile, 1); {Размер блока в один байт}
{$I+}
IORes := IOResult;
if IORes <>0 then
WriteLn('Неправильное имя файла')
until IORes =0;
{ Установка счетчика символов и запись его в файл }
NOfSymb := 0;
WriteAndControl(NofSymb, 4);
{ Задание таблицы смещений кодов символов, запись ее в файл }
Randomize;
for i := 1 to NOfCod do
Codes[i] := Random(256);
WriteAndControl(Codes, NOfCod);
{ Перекодировка символов и запись содержимого полных буферов в файл}
i := 0;
while not Eof(FirstFile) do
begin
Inc(NOfSymb);
Inc(i);
if Eoln(FirstFile) then
begin
Buffer[i] := Chr((13 + Codes[i)) mod 256);
if i = NOfCod then
begin
WriteAndControl(Buffer, NOfCod);
i := 0
end;
Inc(i);
Buffer[i] := Chr((10 + Codes[i]) mod 256);
ReadLn(FirstFile)
end else
begin
Read(FirstFile, Symbol);
Buffer[i] := Chr((Ord(Symbol) + Codes[i]) mod 256)
end;
if i = NOfCod then
begin
WriteAndControl(Buffer, NOfCod);
i := 0
end
end;
{Запись в файл завершающей части символов }
if i <> 0 then
WriteAndControl(Buffer, i);
{ Запись числа символов }
NOfSymb := FileSize(SecondFile) - NOfCod - 4;
Seek(SecondFile, 0);
WriteAndControl(NOfSymb, 4);
{ Завершение программы )
Close(SecondFile};
WriteLn('Конец работы программы');
ReadLn
end.
В этой программе в результирующий файл окончательно будут записаны: общее число
перекодированных символов, таблица смещений кодов символов и перекодированные символы. Файл
используется как файл без типа с размером блока в один байт, который устанавливается
процедурой Rewrite.
11.5. Внешние устройства в качестве файлов
Связь с внешними устройствами в языке Паскаль осуществляется также через файловые переменные.
В Turbo Pascal существует два типа внешних устройств: устройства операционной системы и
текстовые устройства.
Устройства операционной системы, с которыми осуществляется обмен информацией, могут быть
описаны как файлы со стандартными именами. Эти имена следующие:
- CON - консоль (как правило, дисплей). С этим устройством стандартно связаны файловые
переменные Input и Output.
- LPT1, LPT2, LPT3 - печатающие устройства. Если в системе один принтер, он будет иметь имя
LPT1. Если в программе используется стандартный модуль Printer (указан в разделе uses), можно
использовать для вывода на принтер стандартную файловую переменную Lst.
- PRN - синоним LPT1.
- СОМ1, COM2 - имена двух портов.
- AUX - синоним СОМ1.
- NUL - фиктивное внешнее устройство.
К текстовым устройствам относятся устройства, не поддерживаемые операционной системой или
имеющие другие способы обращения. Например, устройство CRT, поддерживаемое стандартным
модулем Crt. Оно эквивалентно CON в операционной системе, но более быстродействующее и
позволяет использовать разные цвета и окна.
С текстовым устройством нельзя связаться процедурой Assign. Для связи с ним служит специальная
модификация этой процедуры, например для связи с устройством CRT следует использовать
процедуру AssignCrt в модуле Crt.