Скачиваний:
56
Добавлен:
08.01.2014
Размер:
2.6 Mб
Скачать

2.1.7. Системный вызовfdwrite

Системный вызов fdwrite противоположен вызову fdread. Он копирует данные из буфера программы, рассматриваемого как массив, во внешний файл.

Описание

uses linux;

Function fdWrite (fd:longint; var buf; size:longint):longint;

Так же, как и вызов fdread, вызов fdwrite имеет три аргумента: дескриптор файла filedes, указатель на записываемые данные buffer и n – положительное число записываемых байтов. Возвращаемое вызовом значение является либо числом записанных символов, либо кодом ошибки -1. Фактически, если возвращаемое значение не равно -1, то оно почти всегда будет равно n. Если оно меньше n, значит, возникли какие-то серьезные проблемы. Например, это может произойти, если в процессе вызова fdwrite было исчерпано свободное пространство на выходном носителе. (Если носитель уже был заполнен до вызова fdwrite, то вызов вернет значение -1.)

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

var

fd:integer;

w1, w2 : longint;

header1: array [0..511] of char;

header2: array [0..1023] of char;

.

.

.

fd := fdopen('newfile', Open_WRONLY or Open_CREAT or Open_EXCL, octal(0644));

if fd = -1 then

exit;

w1 := fdwrite(fd, header1, 512);

w2 := fdwrite(fd, header2, 1024);

дает в результате файл длиной 1536 байт, с содержимым массивов header1 и header2.

Что произойдет, если программа откроет существующий файл на запись и сразу же запишет в него что-нибудь? Ответ очень прост: старые данные в файле будут заменены новыми, символ за символом. Например, предположим, что файл oldhat имеет длину 500 символов. Если программа откроет файл oldhat для записи и выведет в него 10 символов, то первые 10 символов в файле будут заменены содержимым буфера записи программы. Следующий вызов fdwrite заменит очередные 10 символов и так далее. После достижения конца исходного файла в процессе дальнейших вызовов fdwrite его длина будет увеличиваться. Если нужно избежать переписывания файла, можно открыть файл с флагом Open_APPEND. Например:

filedes := fdopen(filename, Open_WRONLY or Open_APPEND);

Теперь в случае успешного вызова fdopen указатель чтения-записи будет помещен, сразу же за последним байтом в файле, и вызов fdwrite будет добавлять данные в конец файла. Этот прием более подробно будет объяснен в разделе 2.1.12.

Программа, демонстрирующая fdOpen, fdwrite и fdCLose.

Uses linux;

Const Line : String[80] = 'This is easy writing !';

Var FD : Longint;

begin

FD:=fdOpen ('Test.dat',Open_WrOnly or Open_Creat);

if FD>0 then

begin

if length(Line)<>fdwrite (FD,Line[1],Length(Line)) then

Writeln ('Error when writing to file !');

fdClose(FD);

end;

end.

2.1.8. Пример copyfile

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

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

Окончательное решение может выглядеть таким образом:

(* Программа copyfile: скопировать файл name1 в файл name2 *)

uses linux;

const

BUFSIZE=512; (* Размер считываемого блока *)

PERM=0644; (* Права доступа для нового файла в форме,

похожей на восьмеричную *)

(* Скопировать файл name1 в файл name2 *)

function copyfile(name1, name2: string):integer;

var

infile, outfile: integer; (*дескрипторы файлов*)

nread: longint;

buffer: array [0..BUFSIZE-1] of byte; (*буфер для чтения/записи*)

begin

infile := fdopen (name1, Open_RDONLY);

if infile=-1 then

begin

copyfile:=-1; (*ошибка открытия первого файла*)

exit;

end;

outfile := fdopen (name2, Open_WRONLY or Open_CREAT or Open_TRUNC, octal(PERM));

if outfile=-1 then

begin

fdclose(infile);

copyfile:=-2; (*ошибка открытия второго файла*)

exit;

end;

(* Чтение из файла name1 по BUFSIZE символов *)

nread := fdread (infile, buffer, BUFSIZE);

while nread > 0 do

begin

(* Записать buffer в выходной файл. *)

if fdwrite (outfile, buffer, nread) < nread then

begin

fdclose (infile);

fdclose (outfile);

copyfile:=-3; (* ошибка записи *)

exit;

end;

nread := fdread (infile, buffer, BUFSIZE);

end;

(*закрываем прочитанные файлы*)

fdclose (infile);

fdclose (outfile);

if (nread = -1) then

copyfile := -4 (* ошибка при последнем чтении *)

else

copyfile := 0; (* все порядке *)

end;

(* Программа для тестирования функции copyfile *)

var

retcode:integer;

begin

if paramcount<2 then

begin

writeln('Используйте: ',paramstr(0),' файл-источник файл-приемник');

exit;

end;

retcode := copyfile(paramstr(1), paramstr(2));

case retcode of

0: writeln('Файл ',paramstr(1),' успешно скопирован в файл ',paramstr(2));

-1: writeln('Ошибка открытия файла ',paramstr(1),' для чтения');

-2: writeln('Ошибка открытия файла ',paramstr(2),' для записи');

-3: writeln('Ошибка записи в файл ',paramstr(2));

-4: writeln('Ошибка чтения из файла ',paramstr(1));

end;

end.

Теперь функцию copyfile можно вызывать так:

retcode := copyfile('squarepeg', 'roundhole');

Упражнение 2.6. Измените функцию copyfile так, чтобы в качестве ее параметров могли выступать дескрипторы, а не имена файлов. Проверьте работу новой версии программы.

Упражнение 2.7. Если вы умеете работать с аргументами командной строки, используйте одну из процедур copyfile для создания программы mycp, копирующей первый заданный в командной строке файл во второй.

Соседние файлы в папке Полищук, Семериков. Системное программирование в UNIX средствами Free Pascal