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

       

Все параметры массива args являются


uses stdio;
/* Все параметры массива args являются указателями.
 * Переменные, на которые они указывают, могут
 * иметь произвольный тип.
 */
function scanf(fmt:pchar; args:array of const):integer;
function fscanf(inf:pfile; fmt:pchar; args:array of const):integer;
function sscanf(str:pchar; fmt:pchar; args:array of const):integer;
Процедуры семейства scanf противоположны по смыслу процедурам семейства printf. Все они принимают ввод из файла (или из строки в случае процедуры sscanf), декодируют его в соответствии с информацией формата fmt и помещают полученные данные в переменные, заданные указателями в массиве args. Указатель файла перемещается на число обработанных символов.
Процедура scanf всегда выполняет чтение из stdin; процедура fscanf выполняет чтение из потока inf; а процедура sscanf выделяется в этом семействе процедур тем, что декодирует строку str и не осуществляет ввода данных. Поскольку последняя процедура работает со строкой в памяти, то она особенно полезна, если некоторую строку ввода нужно анализировать несколько раз.
Строка формата fmt имеет ту же структуру, что и строка формата процедуры printf. Например, следующий оператор считывает очередное целое число из потока стандартного ввода:
var
  inarg:integer;


scanf('%d', [@inarg]);
Важно, что функции scanf передается адрес переменной inarg. Это связано с тем, что, если нужно, чтобы процедура scanf изменяла переменную, которая находится в вызывающей процедуре, следует передать указатель, содержащий адрес этой переменной. Можно очень легко забыть про символ @, что приведет к ошибке записи в память. Новичкам также приходится бороться с искушением помещать знак @
перед всеми указателями, такими как имена символьных массивов.
В общем случае строка формата процедуры scanf может содержать:
–        пробельные символы, то есть пробелы, символы табуляции, перевода строки и страницы. Обычно они соответствуют любым пробельным символам с текущей позиции во входном потоке, до первого не пробельного символа;


–        обычные, не пробельные символы. Они должны точно совпадать с соответствующими символами во входном потоке;
–        спецификации формата. Как упоминалось ранее, они в основном аналогичны спецификациям, используемым в процедуре printf.
Следующий пример показывает использование процедуры scanf с несколькими переменными различных типов:
(* Демонстрационная программа для процедуры scanf *)
uses stdio;
var
  i1, i2:integer;
  fit:float;
  str1, str2:array [0..9] of char;
begin
  scanf('%2d %2d %f %s %s', [@i1, @i2, @flt, pchar(str1), pchar(str2)]);
  .
  .
  .
end.
Первые две спецификации в строке формата сообщают процедуре scanf, что она должна считать два целых числа (в десятичном формате). Так как в обоих случаях ширина поля равна двум символам, предполагается, что первое число должно находиться в двух считанных первыми символах, а второе – в двух следующих (в общем случае ширина поля обозначает максимальное число символов, которое может занимать значение). Спецификации %f
соответствует переменная типа single. Спецификация %s
означает, что ожидается строка, ограниченная пробельными символами. Поэтому, если подать на вход программы последовательность
11 12 34.07 keith ben
то в результате получится следующее:
переменная i1
будет иметь значение 11
переменная i2
будет иметь значение 12
переменная fit будет иметь значение 34.07
строка str1 будет содержать значение keith
строка str2 будет содержать значение ben
Обе строки будут заканчиваться нулевым символом. Обратите внимание, что переменные str1 и str2 должны иметь достаточно большую длину, чтобы в них поместились вводимые строки и нулевой символ в конце. Нельзя передавать процедуре scanf неинициализированный указатель.
Если задана спецификация формата %s, то предполагается, что строка должна быть ограничена пробельными символами. Для считывания строки целиком, включая пробельные символы, необходимо использовать код формата %с. Например, оператор


scanf ('%10c', [pchar(s1)]);
считает любые 10 символов из входного потока и поместит их в массив символов s1. Так как код формата с соответствует пробельным символам, то для получения следующего не пробельного символа должна использоваться спецификация %1s, например:
(* Считать 2 символа, начиная с первого не пробельного*)
scanf('%1s%1c',[@c1,@c2])
Другим способом задания формата строчных данных, не имеющим аналога в формате процедуры printf, является шаблон (scan set). Это последовательность символов, заключенных в квадратные скобки: [ и ]. Входное поле составляется их максимальной последовательности символов, которые попадают в шаблон (в этом случае пробельные символы не игнорируются и не попадают в поле, если они не являются частью шаблона). Например, оператор
scanf('%[ab12]%s',[str1,str2]);
при задании входной строки
2bbaa1other
поместит в строку str1 значение 2bbaa1, а в строку str2 – значение other.
Существует несколько соглашений, используемых при создании шаблона, которые должны быть знакомы пользователям grep или ed. Например, диапазон символов задается строкой вида a-z, то есть [a-d] равносильно [abcd]. Если символ тире (-) должен входить в шаблон, то он должен быть первым или последним символом. Аналогично, если в шаблон должна входить закрывающая квадратная скобка ], то она должна быть первым символом после открывающей квадратной скобки [. Если первым символом шаблона является знак ^, то при этом выбираются только символы, не
входящие в шаблон.
Для присваивания переменных типа longint или double после символа процента в спецификации формата должен находиться символ l. Это позволяет процедуре scanf определять размер параметра, с которым она работает. Следующий фрагмент программы показывает, как можно считать из входного потока переменные обоих типов:
var
  l:longint;
  d:double;
scanf('%ld %lf', [@l, @d]);
Другая ситуация часто возникает, если входной поток содержит больше данных, чем необходимо. Для обработки этого случая спецификация формата может содержать символ (*) сразу же после символа процента, обозначающий данное поле лишним. В результате поле ввода, соответствующее спецификации, будет проигнорировано. Вызов


scanf('%d %*s %*d %s', [@ivar, str]);
для строки ввода
131 cat 132 mat
приведет к присвоению переменной ivar значения 131, пропуску следующих двух полей, а затем присвоению строке str значения mat.
И, наконец, какое значение возвращают функции семейства scanf? Они обычно возвращают число преобразованных и присвоенных полей. Возвращаемое значение может быть равно нулю в случае несоответствия строки формата и вводимых данных. Если ввод прекращается до первого успешно (или неуспешно) введенного поля, то возвращается значение EOF.[19]
Упражнение 11.7. Напишите программу, выводящую в шестнадцатеричной и восьмеричной форме свои аргументы, которые должны быть десятичными целыми числами.
Упражнение 11.8. Напишите программу savematrix, которая должна сохранять в файле матрицу целых чисел произвольного размера в удобочитаемом формате, и программу readmatrix, которая загружает матрицу из файла. Используйте для этого только процедуры fprintf и fscanf. Постарайтесь свести к минимуму число пробельных символов (пробелы, символы табуляции и др.) в файле. Совет: используйте для задания формата записи в файл символ переменной ширины (*).

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