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

и fclose являются эквивалентами вызовов


uses stdio;
function fopen(filename:pchar; _type:pchar):pfile;
function fclose(_stream:pfile):integer;
Библиотечные процедуры fopen и fclose являются эквивалентами вызовов fdopen и fdclose. Процедура fopen открывает файл, заданный параметром filename, и связывает с ним структуру TFILE. В случае успешного завершения процедура fopen возвращает указатель на структуру TFILE, идентифицирующую открытый файл, объект PFILE также часто называют открытым потоком ввода/вывода (эта структура FILE является элементом внутренней таблицы). Процедура fclose закрывает файл, заданный параметром _stream, и, если этот файл использовался для вывода, также сбрасывает на диск все данные из внутреннего буфера.
В случае неудачи процедура fopen возвращает нулевой указатель nil, определенный в файле system. В этом случае, так же как и для вызова fdopen, переменная linuxerror будет содержать код ошибки, указывающий на ее причину.
Второй параметр процедуры fopen указывает на строку, определяющую режим доступа. Она может принимать следующие основные значения:



r
Открыть файл filename только для чтения. (Если файл не существует, то вызов завершится неудачей и процедура fopen вернет нулевой указатель nil)
w
Создать файл filename и открыть его только для записи. (Если файл уже существует, то он будет усечен до нулевой длины)
а
Открыть файл filename только для записи. Все данные будут добавляться в конец файла. Если файл не существует, он создается

Файл может быть также открыт для обновления, то есть программа может выполнять чтение из файла и запись в него. Другими словами, программа может одновременно выполнять для файла и операции ввода, и операции вывода без необходимости открывать его заново. В то же время из-за механизма буферизации такой ввод/вывод будет более ограниченным, чем режим чтения/записи, поддерживаемый вызовами fdread и fdwrite. В частности, после вывода нельзя осуществить ввод без вызова одной из стандартных процедур ввода/вывода fseek или rewind. Эти процедуры изменяют положение внутреннего указателя чтения/ записи и обсуждаются ниже. Аналогично нельзя выполнить вывод после ввода без вызова процедур fseek или rewind или процедуры ввода, которая перемещает указатель в конец файла. Режим обновления обозначается символом +


в конце аргумента, передаваемого процедуре fopen. Вышеприведенные режимы можно дополнить следующим образом:

r+
Открыть файл filename для чтения и записи. Если файл не существует, то вызов снова завершится неудачей
w+
Создать файл filename и открыть его для чтения и записи. (Если файл уже существует, то он будет усечен до нулевой длины)
а+
Открыть файл filename для чтения и записи. При записи данные будут добавляться в конец файла. Если файл не существует, то он создается

В некоторых системах для доступа к двоичным, а не текстовым файлам, к строке также нужно добавлять символ b, например, rb.
Если файл создается при помощи процедуры fopen, для него обычно устанавливается код доступа octal(0666). Это позволяет всем пользователям выполнять чтение из файла и запись в него. Эти права доступа по умолчанию могут быть изменены установкой ненулевого значения атрибута процесса umask. (Системный вызов umask был изучен в главе 3.)
Следующий пример программы показывает использование процедуры fopen и ее связь с процедурой fclose. При этом, если файл indata существует, то он открывается для чтения, а файл outdata создается (или усекается до нулевой длины, если он существует). Процедура fatal предназначена для вывода сообщения об ошибке, ее описание было представлено в предыдущих главах. Она просто передает свой аргумент процедуре perror, а затем вызывает halt для завершения работы программы.
uses stdio;
const
  inname:pchar = 'indata';
  outname:pchar = 'outdata';
function fatal(s:pchar):integer;
begin
  perror (s);
  halt (1);
end;
var
  inf,outf:pfile;
begin
  inf := fopen (inname, 'r');
  if inf = nil then
    fatal ('Невозможно открыть входной файл');
  outf := fopen (outname, 'w');
  if outf = nil then
    fatal ('Невозможно открыть выходной файл');
  (* Выполняются какие-либо действия ... *)
  fclose (inf);
  fclose (outf);
  halt (0);
end.
На самом деле, в данном случае оба вызова fсlose не нужны. Дескрипторы, связанные с файлами inf и outf, будут автоматически закрыты при завершении работы процесса, и вызов halt автоматически сбросит данные из буфера указателя outf на диск, записав их в файл outdata.
С процедурой fclose тесно связана процедура fflush:


uses stdio;
function fflush(_stream:pfile):integer;
Выполнение этой процедуры приводит к сбросу на диск содержимого буфера вывода, связанного с потоком _stream. Другими словами, данные из буфера записываются в файл немедленно, независимо от того, заполнен буфер или нет. Это гарантирует, что содержимое файла на диске будет соответствовать тому, как он выглядит с точки зрения процесса. (Процесс считает, что данные записаны в файл с того момента, как они оказываются в буфере, поскольку механизм буферизации прозрачен.) Любые данные из буфера ввода этим вызовом предусмотрительно отбрасываются.
Поток _stream остается открытым после завершения процедуры fflush. Как и процедура fclose, процедура fflush возвращает постоянную EOF в случае ошибки и нулевое значение – в случае успеха. (Значение постоянной EOF задано в файле
stdio равным –1. Оно обозначает конец файла, но может также использоваться для обозначения ошибок.)


uses stdio;
function getc(inf:pfile):integer;
function putc(c:integer; outf:pfile):integer;
Наиболее простыми из процедур стандартной библиотеки ввода/вывода являются процедуры getc и putc. Процедура getc возвращает очередной символ из входного потока inf. Процедура putc помещает символ, обозначенный параметром с, в выходной поток outf.
В обеих процедурах символ с имеет тип integer, а не char, что позволяет процедурам использовать наборы 16-битовых «широких» символов. Это также позволяет процедуре getc возвращать значение –1, находящееся вне диапазона возможных значений типа char. Постоянная EOF используется процедурой getc для обозначения того, что либо достигнут конец файла, либо произошла ошибка. Процедура putc также может возвращать значение EOF в случае ошибки.
Следующий пример является новой версией процедуры
copyfile, представленной в главе 2; в данном случае вместо использования вызовов
fdread и fdwrite используются процедуры getc и putc:
uses stdio;
(* Скопировать файл f1 в файл f2
 * при помощи стандартных процедур ввода/вывода
 *)
function copyfile(const f1, f2:pchar):integer;
var
  inf, outf:pfile;
  c:longint;
begin
  inf := fopen (f1, 'r');
  if inf = nil then
  begin
    copyfile:=-1;
    exit;
  end;
  outf := fopen (f2, 'w');
  if outf = nil then
  begin
    fclose (inf);
    copyfile:=-2;
    exit;
  end;
  c := getc (inf);
  while c <> EOF do
  begin
    putc (c, outf);
    c := getc (inf);
  end;
  fclose (inf);
  fclose (outf);
  copyfile:=0;
end;
Копирование выполняет внутренний цикл while. Снова обратите внимание на то, что переменная с
имеет тип longint, а не char.
Упражнение 11.1. В упражнениях 2.4 и 2.5 мы описали программу count, которая выводит число символов, слов и строк во входном файле. (Напомним, что слово определялось, как любая последовательность алфавитно-цифровых символов или одиночный пробельный символ.) Перепишите программу count, используя процедуру getc.
Упражнение 11.2. Используя процедуру getс, напишите программу, выводящую статистику распределения символов в файле, то есть число раз, которое встречается в файле каждый символ. Один из способов сделать это состоит в использовании массива целых чисел типа long, который будет содержать счетчики числа символов, а затем рассматривать значение каждого символа в качестве индекса увеличиваемого счетчика массива. Программа также должна рисовать простую гистограмму полученного распределения при помощи процедур printf и putc.


uses stdio;
function ungetc(c:integer; _stream:pfile):integer;
Процедура ungetc возвращает символ с в поток _stream. Это всего лишь логическая операция. Входной файл не будет при этом изменяться. В случае успешного завершения процедуры ungetc символ с будет следующим символом, который будет считан процедурой getc. Гарантируется возврат только одного символа. В случае неудачной попытки вернуть символ с процедура ungetc возвращает значение EOF. Попытка вернуть сам символ EOF должна всегда завершаться неудачей. Но это обычно не представляет проблемы, так как все последующие вызовы процедуры getc после достижения конца файла приведут к возврату символа EOF.
Обычно процедура ungetc используется для восстановления исходного состояния входного потока после чтения лишнего символа для проверки условия. Следующая процедура getword применяет это простой подход для ввода строки, которая содержит либо непрерывную последовательность алфавитно-цифровых символов, либо одиночный нетекстовый символ. Конец файла кодируется возвращенным значением nil. Процедура getword принимает в качестве аргумента указатель на структуру TFILE. Она использует для проверки два макроса, определенные в файле stdio. Первый из них, isspace, определяет, является ли символ пробельным символом, таким как символ пробела, табуляции или перевода строки. Второй, isalnum, проверяет, является ли символ алфавитно-цифровым, то есть цифрой или буквой.
(* В этом файле определены isspace и isalnum *)
uses stdio;
const
  MAXTOK=256;
var
  inbuf:array [0..MAXTOK] of char;
function getword (inf:pfile):pchar;
var
  c,count:integer;
begin
  count:=0;
  (* Удалить пробельные символы *)
  repeat
    c := getc (inf);
  until not isspace (c);
  if c = EOF then
  begin
    getword:=nil;
    exit;
  end;
  if not isalnum (c) then    (* символ не является алфавитно-цифровым *)
  begin
    inbuf[count] := char(c);
    inc(count);
  end
  else
  begin
    (* Сборка "слова" *)


    repeat
      if count < MAXTOK then
      begin
        inbuf[count] := char(c);
        inc(count);
      end;
      c := getc (inf);
    until not isalnum (c);
    ungetc (c, inf);         (* вернуть символ *)
  end;
  inbuf[count] := #0;
  (* нулевой символ в конце строки *)
  getword:=inbuf;
end;
var
  word:pchar;
begin
  while true do
  begin
    word := getword (stdin);
    if word <> nil then
      puts (word)
    else
      break;
  end;
end.
Если подать на вход программы следующий ввод
Это данные
  на входе
 программы!!!
то процедура getword вернет следующую последовательность строк:
Это
данные
на
входе
программы
!
!
!
Упражнение 11.3. Измените процедуру getword так, чтобы она распознавала также числа, которые могут начинаться со знака минус или плюс и могут содержать десятичную точку.

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