Процедура gets считывает последовательность символов
uses stdio;
function gets(buf:pchar):pchar;
function fgets(buf:pchar; nsize:integer; inf:pfile):pchar;
Процедура gets считывает последовательность символов из потока стандартного ввода (stdin), помещая все символы в буфер, на который указывает аргумент buf. Символы считываются до тех пор, пока не встретится символ перевода строки или конца файла. Символ перевода строки newline отбрасывается, и вместо него в буфер buf помещается нулевой символ, образуя завершенную строку. В случае возникновения ошибки или при достижении конца файла возвращается значение nil.
Процедура fgets является обобщенной версией процедуры gets. Она считывает символы из потока inf в буфер buf до тех пор, пока не будет считано nsize-1 символов или не встретится раньше символ перевода строки newline или не будет достигнут конец файла. В процедуре fgets символы перевода строки newline не отбрасываются, а помещаются в конец буфера (это позволяет вызывающей функции определить, в результате чего произошел возврат из процедуры fgets). Как и процедура gets, процедура fgets возвращает указатель на буфер buf в случае успеха и nil – в противном случае.
Процедура gets является довольно примитивной. Так как она не знает размер передаваемого буфера, то слишком длинная строка может привести к возникновению внутренней ошибки в процедуре. Чтобы избежать этого, можно использовать процедуру fgets (для стандартного ввода stdin).
Следующая процедура yesno использует процедуру fgets для получения положительного или отрицательного ответа от пользователя; она также вызывает макрос isspace для пропуска пробельных символов в строке ответа:
(* Процедура yesno - получить ответ от пользователя *)
uses stdio;
const
YES=1;
NO=0;
ANSWSZ=80;
pdefault:pchar = 'Наберите "y" (YES), или "n" (NO)';
error:pchar = 'Неопределенный ответ';
function yesno (prompt:pchar):integer;
var
buf:array [0..ANSWSZ-1] of char;
p_use, p:pchar;
begin
(* Вывести приглашение, если он не равно nil.
uses stdio;
function puts(str:pchar):integer;
function fputs(str:pchar; outf:pfile):integer;
Процедура puts записывает все символы (кроме завершающего нулевого символа) из строки str на стандартный вывод (stdout). Процедура fputs записывает строку str в поток outf. Для обеспечения совместимости со старыми версиями системы процедура puts добавляет в конце символ перевода строки, процедура же fputs не делает этого. Обе функции возвращают в случае ошибки значение EOF.
Следующий вызов процедуры puts приводит к выводу сообщения Hello, world на стандартный вывод, при этом автоматически добавляется символ перевода строки newline:
puts('Hello, world');
uses stdio;
function fread(buffer:pointer; size, nitems:longint; inf:pfile):longint;
function fwrite(buffer:pointer; size,nitems:longint; outf:pfile):longint;
Эти две полезные процедуры обеспечивают ввод и вывод произвольных нетекстовых данных. Процедура fread считывает nitems объектов данных из входного файла, соответствующего потоку inf. Считанные байты будут помещены в массив buffer. Каждый считанный объект представляется последовательностью байтов длины size. Возвращаемое значение дает число успешно считанных объектов.
Процедура fwrite является точной противоположностью процедуры fread. Она записывает данные из массива buffer в поток outf. Массив buffer содержит nitems объектов, размер которых равен size. Возвращаемое процедурой значение дает число успешно записанных объектов.
Эти процедуры обычно используются для чтения и записи содержимого произвольных структур данных языка Паскаль. При этом параметр size часто содержит конструкцию sizeof, которая возвращает размер структуры в байтах.
Следующий пример показывает, как все это работает. В нем используется шаблон структуры dict_elem. Экземпляр этой структуры может представлять собой часть записи простой базы данных. Используя терминологию баз данных, структура dict_elem представляет собой запись, или атрибут, базы данных. Мы поместили определение структуры dict_elem в заголовочный файл dict.inc, который выглядит следующим образом:
(* dict.inc - заголовочный файл для writedict и readdict *)
uses stdio;
(* Структура dict_elem элемент данных *)
(* (соответствует полю базы данных) *)
type dict_elem=record
d_name:array [0..14] of char; (* имя элемента словаря *)
d_start:integer; (* начальное положение записи *)
d_length:integer; (* длина поля *)
d_type:integer; (* обозначает тип данных *)
end;
pdict_elem=^dict_elem;
const
ERROR=-1;
SUCCESS=0;
He вдаваясь в смысл элементов структуры, введем две процедуры writedict и readdict, которые соответственно выполняют запись и чтение массива структур dict_elem. Файлы, создаваемые при помощи этих двух процедур, можно рассматривать как простые словари данных для записей в базе данных.
Процедура writedict имеет два параметра, имя входного файла и адрес массива структур dict_elem. Предполагается, что этот список заканчивается первой структурой массива, в которой элемент d_length равен нулю.
{$i dict.inc}
function writedict (const dictname:pchar; elist:pdict_elem):integer;
var
j:integer;
outf:pfile;
begin
(* Открыть входной файл *)
outf := fopen (dictname, 'w');
if outf = nil then
begin
writedict:=ERROR;
exit;
end;
(* Вычислить размер массива *)
j:=0;
while elist[j].d_length <> 0 do
inc(j);
(* Записать список структур dict_elem *)
if fwrite (elist, sizeof (dict_elem), j, outf) < j then
begin
fclose (outf);
writedict:=ERROR;
exit;
end;
fclose (outf);
writedict:=SUCCESS;
end;
Обратите внимание на использование sizeof(dict_elem) для сообщения процедуре fwrite размера структуры dict_elem в байтах.
Процедура readdict использует процедуру fread для считывания списка структур из файла. Она имеет три параметра: указатель на имя файла словаря indictname, указатель inlist на массив структур dict_elem, в который будет загружен список структур из файла, и размер массива maxlength.
function readdict (const indictname:pchar;inlist:pdict_elem;
maxlength:integer):pdict_elem;
var
i:integer;
inf:pfile;
begin
(* Открыть входной файл *)
inf := fopen (indictname, 'r');
if inf = nil then
begin
readdict:=nil;
exit;
end;
(* Считать структуры dict_elem из файла *)
for i:=0 to maxlength - 1 do
if fread (@inlist[i], sizeof (dict_elem), 1, inf) < 1 then
break;
fclose (inf);
(* Обозначить конец списка *)
inlist[i].d_length := 0;
(* Вернуть начало списка *)
readdict:=inlist;
end;
const
delem1:array [0..1] of dict_elem=(
(d_name:('d','n','a','m','e', #0,#0,#0,#0,#0,#0,#0,#0,#0,#0);
d_start:2; d_length:15; d_type:3),
(d_name:(#0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0)
d_start:0; d_length:0; d_type:0)
);
var
delem2:array [0..1] of dict_elem;
begin
printf ('delem1: d_name=%s, d_start=%d, d_length=%d, d_type=%d'#$a,
[pchar(delem1[0].d_name), delem1[0].d_start, delem1[0].d_length,
delem1[0].d_type]);
writedict ('dictionary', @delem1[0]);
if readdict ('dictionary', @delem2[0], 2)<>nil then
printf ('delem2: d_name=%s, d_start=%d, d_length=%d, d_type=%d'#$a,
[pchar(delem2[0].d_name), delem2[0].d_start, delem2[0].d_length,
delem2[0].d_type]);
end.
И снова обратите внимание на приведение типа и использование конструкции sizeof.
Необходимо сделать важную оговорку. Бинарные данные, записываемые в файл при помощи процедуры fwrite, отражают внутреннее представление данных в системной памяти. Так как это представление зависит от архитектуры компьютера и различается порядком байтов в слове и выравниванием слов, то данные, записанные на одном компьютере, могут не читаться на другом, если не предпринять специальные усилия для того, чтобы они были записаны в машинно-независимом формате. По тем же причинам почти всегда бессмысленно выводить значения адресов и указателей.
И последний момент: можно было бы получить практически тот же результат, напрямую используя вызовы fdread или fdwrite, например:
fdwrite(fd, ptr, sizeof(dict_elem));
Основное преимущество версии, основанной на стандартной библиотеке ввода/вывода, снова заключается в ее лучшей эффективности. Данные при этом будут читаться и записываться большими блоками, независимо от размера структуры dict_elem.
Упражнение 11.6. Представленные версии процедур writedict и readdict работают с файлами словаря, которые могут содержать только один тип записей. Измените их так, чтобы в одном файле можно было хранить информацию о нескольких типах записей. Другими словами, нужно, чтобы файл словаря мог содержать несколько независимых именованных списков структур dict_elem. (Совет: включите в начало файла «заголовок»; содержащий информацию о числе записей и типе полей.)