- •1 0. Организация работы с файлами
- •А) чтение порции информации с диска в буфер файла б), в), г), д) последовательное чтение информации до конца буфера без обращения к диску
- •Е) чтение последней порции информации в буфер файла ж), з), и), к) последовательное чтение информации из буфера без обращения к диску
- •10.1. Задания для самостоятельного выполнения
- •26. Дан символьный файл f. Считая, что количество символов в слове (см. Задачу № 24) не превосходит двадцати:
- •27. Дан символьный файл f, содержащий сведения о сотрудниках учреждения, записанный по следующему образцу:
Рисунок
10.2 – Организация последовательного
доступа к файлу
б)
д)
г)
в)
Е
Е
х
Е
х
а
Е х а л г р е к а ч е р е з р
а л г р е к а ч е р е з р
х а л г р е к а ч е р е з р
л
г
р
е
к
ч
Е
х
а
а
е
р
е
з
р
А) чтение порции информации с диска в буфер файла б), в), г), д) последовательное чтение информации до конца буфера без обращения к диску
Рисунок 10.2
(продолжение) - Организация последовательного
доступа к файлу
ж)
к)
и)
з)
е
к у
г
р
е
к
у
ц
а
п
. ^z
л
г
р
е
ч
Е
х
а
к
а
е
р
е
з
р
е
к
у
,
в
и
д
и
т
к
а
р
у
к
е
к
^z
л
г
р
е
ч
Е
х
а
а
е
р
е
з
р
е
к
у
,
в
и
д
и
т
к
а
р
у
к
у
г
р
е
к
у
ц
а
п
.
е
л
г
р
е
ч
Е
х
а
к
а
е
р
е
з
р
е
к
у
,
в
и
д
и
т
к
р
к
у к у
г
р
е
к
у
ц
а
п . ^z
л
г
р
е
ч
Е
х
а
а
е
р
е
з
р
е
к
у
,
в
и
д
и
т
к
а
в
р
у
е
к
е)
р у к у
г
р
е
к
у
ц
а
п
. ^z
л
г
р
е
ч
Е
х
а
а
е
р
е
з
р
е
к
у
в
и
д
и
т
к
а
,Е) чтение последней порции информации в буфер файла ж), з), и), к) последовательное чтение информации из буфера без обращения к диску
1. Типизированный текстовый файл.
2. Типизированный нетекстовый файл
3. Нетипизированный файл
При использовании типизированных файлов контроль корректности и интерпретация читаемой/записываемой информации осуществляется встроенными средствами операторов ввода-вывода языка программирования. Не типизированные файлы предоставляют программисту большую свободу действий, но в то же время вся ответственность за корректность обработки информации ложится на его плечи. Поэтому не типизированные файлы рекомендуется использовать исключительно для информации, не имеющей четковыраженной периодически повторяющейся структуры. В таблице ниже приведено несколько примеров определения переменных файлового типа.
№ п.п. |
Фрагмент программы |
Комментарии |
1. |
var A,B: Text; |
Во фрагменте приведено описание двух буферов, каждый из которых предназначен для работы с файлами, содержащими текстовую информацию |
2. |
var F: file of Real; P: file of Integer; |
Во фрагменте приведено описание двух буферов, первый из которых (F) предназначен для работы с файлом, содержащим только действительные числа, второй (P)– с файлом, элементы которого рассматриваются как целые числа. |
3. |
var T: file of array[1..15] of Real; |
Во фрагменте приведено описание файла, элементами которого являются массивы, содержащие 15 действительных чисел. Такой файл совпадает во многом с описанием буфера F в предыдущем примере. Отличие заключается в том, что в последнем примере общее количество чисел в файле всегда будет кратно 15. |
4. |
var T: array[1..8] of file of Integer; |
Интересная конструкция – объявлен массив, элементами которого являются буферы, предназначенные для работы с целочисленными файлами. |
5. |
var G: file; |
В буфер G вы имеете право поместить любую информацию, но следить за тем, что считывается та же структура, что и записывается – это уже ваша проблема. Вы записали в файл действительное число, а читаете его как целое – то, что это скорее всего будет ошибкой – полбеды, беда в том, что эту ошибку будет весьма нелегко обнаружить. |
Объявляя буфер файла, мы только отводим под него определенное место в ОЗУ, но пока никак не ассоциируем этот буфер с каким-либо файлом на диске. Для этих целей в Pascal'е предусмотрена отдельная операция, связывающая буфер с конкретным файлом:
После выполнения этой операции буфер и файл оказываются связанными. Теперь вам нет необходимости работать непосредственно с файлом – в вашем распоряжении его отображение в ОЗУ – буфер. За связь буфера с физическим файлом теперь отвечает драйвер. Использование буфера создает иллюзию непосредственной работы с файлом, поэтому в литературе вместо термина «буфер» часто используется термин «логический файл», а иногда даже просто «файл». Последнее – неверно в корне и может служить источником серьезных ошибок, в том числе потери информации. Принцип асимметричного дуализма языкового знака1 на языки программирования следует распространять с очень большой осторожностью.
Процедура RESET осуществляет открытие на диске существующего файла, считывает в буфер первую порцию информации и устанавливает указатель на начало буфера. Дело в том, что буфер организован по принципу очереди: читать из него можно информацию только последовательно: очередное значение не может быть прочитано до того, как будут прочитаны объекты, хранящихся в файле (а, следовательно, и в буфере) перед ним.
Процедура REWRITE осуществляет открытие нового файла. При этом если на диске уже есть файл с таким же именем, то старая версия будет удалена из каталога. Запись в диск тоже осуществляется последовательно по мере поступления информации в буфер: напрямую вставить объект в середину буфера невозможно – необходимо предварительно перезаписать всю имеющуюся информацию.
Только после того, как файлу был назначен буфер (операция ASSIGN) и он был соответствующим образом открыт (операции RESET или REWRITE) с ним можно выполнять операции ввода/вывода:
Оператор вызова процедуры READ считывает из буфера информацию и передает ее соответствующим переменным. Принцип работы оператора чтения из файла точно такой же, как и у рассмотренного в главе 4 оператора чтения с клавиатуры. Разницы между ними нет практически никакой – просто, если идентификатор буфера не указан, то по умолчанию при чтении процедура READ обращается к буферу клавиатуры1. По аналогии определим оператор-процедуру записи в файл:
После окончания работы с файлом его следует закрыть:
Операция CLOSE освобождает буфер файла (записывает содержимое буфера в физический файл на диске) и отменяет действие оператора RESET (REWRITE). По большому счету операция CLOSE не является обязательной для автономного программного приложения, т.к. оператор завершения программы «END.» закрывает все открытые файлы. Тем не менее, эту операцию следует обязательно прописывать во избежание потери информации, хранящейся в буфере (но еще не записанной в файл на диске), в аварийной ситуации (например, при сбое питания).
Мы рассмотрели минимальное2 множество операций, необходимых при работе с файлом. Теперь попробуем применить их на практике. Для этого вернемся к заданию 6.2, немного его модифицировав:
Задание 10.1.
Постановка задачи
Составить
таблицу значений функции
для
построения графика при x,
изменяющемся
от xн
до xк
с шагом dx.
Выбор метода решения и проектирование
Программа, написанная нами в главе 6 всем хороша кроме одного – при большом количестве расчетов полученная информация не умещается на экране дисплея и поэтому ее лучше выводить не на экран, а в текстовый файл на диске, что мы и сделаем, принципиально не меняя алгоритм.
Т
екст
программы
program WriteToTextFile;
v
ar
x0, x1, x, y, Dx: Integer;
F: Text;
Begin
Assign(F,'result.txt');
ReWrite(F);
Write(‘x0, Dx, x1= ?’);
ReadLn(x0,Dx,x1);
x:=x0;
repeat
y:= Sqr(x);
WriteLn(F,‘x= ’,x:5,’ y= ’,y:10);
x:= x+Dx;
until x> x1;
Close(F);
ReadLn;
End.
Сколько бы строк ни было в файле result.txt мы всегда можем его просмотреть, отредактировать, распечатать и т.д. с помощью обычного текстового редактора.
П
опробуем
эту же задачу решить, используя
типизированный нетекстовый файл.
Очевидно, что этот файл уже не будет
содержать текстовых комментариев, его
нельзя будет просмотреть и обработать
с помощью стандартного текстового
редактора, зато данные из него можно
будет использовать в дальнейшем для
работы другой программы. Да и места на
диске он будет занимать гораздо меньше.
p
rogram
WriteToIntegerFile;
var
x0,x1,x,y,Dx:Integer;
F: file of Integer;
B
egin
Assign(F,'result.dat');
ReWrite(F);
Write(‘x0, Dx, x1= ?’);
ReadLn(x0,Dx,x1);
x:=x0;
repeat
y:=Sqr(x);
WriteLn(‘x= ’,x,’y= ’,y);
Write(F,x,y);
x:=x+Dx;
until
x>x1;
Close(F);
ReadLn;
End.
В завершение проведем небольшой вычислительный эксперимент. Суть его в следующем. В текстовом файле через пробел записаны целые числа от 10 до 16. Попытаемся его прочитать различными способами:
1) символами (Char) из текстового файла (Text),
2) цулыми числами (Integer) из текстового файла (Text),
3) числами Real из текстового файла (Text),
4) действительными числами (Integer) из целочисленного файла (file of Integer).
Результаты каждой операции чтения необходимо вывести на экран через запятую.
Написание текста программы не представляет особых сложностей:
program Experiment;
var
F1:Text; X1:Char;
F2:file of Integer;
X2:Integer;
X3:Real
Begin
Assign(F1,'my_file.dat');
Reset(F1);
While Not EOF(F1) Do
Begin
Read(F1,X1);
Write(X1,',');
End;
Close(F1);
ReadLn;
Reset(F1);
While Not EOF(F1) Do
Begin
Read(F1,X2);
Write(X2,',');
End;
Close(F1);
ReadLn;
Reset(F1);
While Not EOF(F1) Do
Begin
Read(F1,X3);
Write(X3,',');
End;
Close(F1);
ReadLn;
Assign(F2,'my_file.dat');
Reset(F2);
While Not EOF(F2) Do
Begin
Read(F2,X2);
Write(X2,',');
End;
ReadLn;
Close(F2);
End.
В результате расчетов получаем:
1,0,,1,1,,1,2,,1,3,,1,4,,1,5,,1,6,
10,11,12,13,14,15,16,
10.00,11.00,12.00,13.00,14.00,15.00,16.00,
12337,12576,8241,12849,12576,8243,13361,12576,8245,13873,
Если полученные результаты вас обескураживают – у вас проблемы с пониманием представления информации в ЭВМ. Не смертельно, но лечиться надо1. Рецепт приведен ниже.
С первой строчкой все должно быть понятно – вспомните, как вы создавали файл: загрузили текстовый редактор, а потом начали набирать символы: один (‘1’), ноль (‘0’)2, пробел (‘’), единицу (‘1’) и т.д. Наконец, шестнадцать – это 2 символа: единица (‘1’) и шесть (‘6’). Так что с первой строкой все правильно: 20 символов по 1 байту ввели – столько же и прочитали. За что купили – за то и продали.
Со второй и третьей строчками несколько сложнее: в файле, как мы только что выяснили, содержались символы (изображения целых чисел с помощью арабских цифр) и когда оператор Read считывал эти символы – он автоматически приводил их к нужному типу3. В первом случае к типу Integer, во втором – Real. Следует иметь в виду, подобное возможно не всегда. Как будет видно из примеров следующей главы, при чтении данных структурного типа программисту приходится рассчитывать только на собственные силы и не уповать на приведение.
Наибольшие затруднения вызывает четвертая строка. Чтобы разобраться в ней придется вспомнить, что вся информация в памяти ЭВМ хранится в двоичном виде. При этом число типа Integer занимает 2 байта, Real – 6 байт, а символ Char – 1 байт. Не трудно подсчитать, что файл содержит полезную информацию объемом 20 байт1. Таким образом, мы сможем из него прочитать [20/1]=20 символов (Char), рассматривая его как Text; [20/2]=10 элементов типа Integer, рассматривая его как file of Integer и [20/6]=3 элемента типа Real, рассматривая file of Real2. Так что, по количеству элементов в четвертой строке вопросов не должно быть. Что касается ее содержания, то, давайте представим несколько первых чисел из четвертой строки в двоичном коде и выделим в них фрагменты по 1 байту:
1233710= 110000001100012= 00110000 001100012,
1257610= 110001001000002= 00110001 001000002,
0824110= 100000001100012= 00100000 001100012,
1284910= 110010001100012= 00110010 001100012.
Заметим, что
001000002=3210 – не что иное, как код символа пробел ‘’,
001100002=4810 – код символа ‘0’,
001100012=4910 – код символа ‘1’,
001100102=5010 – код символа ‘2’,
001100112=5110 – код символа ‘3’.
Дальнейшее пережевывание этого примера представляется не только не целесообразным, но и вредным3, т.к. ведет, по словам знакомого вам по главе 8 старшины, к застою «бицепсов правого и левого полушария».
