Системное программирование в UNIX средствами Free Pascal

         

Процедура 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. (Совет: включите в начало файла «заголовок»; содержащий информацию о числе записей и типе полей.)

Содержание раздела