Пример: гостиница
В качестве несколько надуманного, но возможно наглядного примера, предположим, что имеется файл residents, в котором записаны фамилии постояльцев гостиницы. Первая строка содержит фамилию жильца комнаты 1, вторая – жильца комнаты 2 и т.д. (очевидно, это гостиница с прекрасно организованной системой нумерации комнат). Длина каждой строки составляет ровно 41 символ, в первые 40 из которых записана фамилия жильца, а 41-й символ является символом перевода строки для того, чтобы файл можно было вывести на дисплей при помощи команды UNIX cat.
Следующая функция getoccupier вычисляет по заданному целому номеру комнаты положение первого байта фамилии жильца, затем перемещается в эту позицию и считывает данные. Она возвращает либо указатель на строку с фамилией жильца, либо нулевой указатель в случае ошибки (мы будем использовать для этого значение nil). Обратите внимание, что мы присвоили переменной дескриптора файла infile исходное значение –1. Благодаря этому мы можем гарантировать, что файл будет открыт всего один раз.
(* Функция getoccupier - получить фамилию из файла residents *)
uses linux;
const
NAMELENGTH=41;
var
namebuf:array [0..NAMELENGTH-1] of char; (* Буфер для фамилии *)
const
infile:integer=-1; (* Для хранения дескриптора файла *)
function getoccupier(roomno:integer):pchar;
var
offset, nread:longint;
begin
(* Убедиться, что файл открывается впервые *)
if infile = -1 then
begin
infile := fdopen ('residents', Open_RDWR);
if infile = -1 then
begin
getoccupier := nil; (* Невозможно открыть файл *)
exit;
end;
end;
offset := (roomno - 1) * NAMELENGTH;
(* Найти поле комнаты и считать фамилию жильца *)
if fdseek (infile, offset, SEEK_SET) = -1 then
begin
getoccupier := nil;
exit;
end;
nread := fdread (infile, namebuf, NAMELENGTH);
if nread <= 0 then
begin
getoccupier := nil;
exit;
end;
(* Создать строку, заменив символ перевода строки на '\0' *)
namebuf[nread - 1] := #0;
getoccupier := namebuf;
end;
Если предположить, что в гостинице 10 комнат, то следующая программа будет последовательно вызывать функцию getoccupier для просмотра файла и выводить каждую найденную фамилию при помощи процедуры writeln из стандартного модуля system:
(* Программа listoc выводит все фамилии жильцов *)
const
NROOMS=10;
var
j:integer;
p:pchar;
begin
for j := 1 to NROOMS do
begin
p := getoccupier (j);
if p<>nil then
writeln('Комната ', j:2, ', ', p)
else
writeln('Ошибка для комнаты ', j);
end;
end.
Упражнение 2.9. Придумайте алгоритм для определения пустых комнат. Измените функцию getoccupier и файл данных, если это необходимо, так, чтобы он отражал эти изменения. Затем напишите процедуру с названием findfree для поиска свободной комнаты с наименьшим номером.
Упражнение 2.10. Напишите процедуру freeroom для удаления записи о жильце. Затем напишите процедуру addguest для внесения новой записи о жильце, с предварительной проверкой того, что выделяемая комната свободна.
Упражнение 2.11. Объедините процедуры getoccupier, freeroom, addguest и findfree в простой программе с названием frontdesk, которая управляет файлом данных. Используйте аргументы командной строки или напишите интерактивную программу, которая вызывает функции writeln и readln. В обоих случаях для вычисления номера комнаты вам потребуется преобразовывать строки в целые числа. Вы можете использовать для этого библиотечную процедуру StrToInt:
i := StrToInt(str);
где string – указатель на строку символов, а i
– целое число.
Упражнение 2.12. В качестве обобщенного примера напишите программу на основе системного вызова fdseek, которая копирует в обратном порядке байты из одного файла в другой. Насколько эффективным получилось ваше решение?
Упражнение 2.13. Используя вызов fdseek, напишите процедуры для копирования последних 10 символов, последних 10 слов и последних 10 строк из одного файла в другой.