Опубликован: 05.07.2006 | Уровень: для всех | Доступ: платный
Лекция 8:

Ввод и вывод

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >

7.5. Форматное преобразование в памяти

От функции scanf и printf происходят функции sscanf и sprintf, которые осуществляют аналогичные преобразования, но оперируют со строкой, а не с файлом. Обращения к этим функциям имеют вид:

sprintf(string, control, arg1, arg2, ...) 
sscanf(string, control, arg1, arg2, ...)

Как и раньше , функция sprintf преобразует свои аргументы arg1, arg2 и т.д. В соответствии с форматом, указанным в control, но помещает результаты в string, а не в стандартный вывод. Kонечно, строка string должна быть достаточно велика, чтобы принять результат. Например, если name - это символьный массив, а n - целое, то

sprintf(name, "temp%d", n);

создает в name строку вида tempnnn, где nnn - значение n.

функция sscanf выполняет обратные преобразования - она просматривает строку string в соответствии с форматом в аргументе control и помещает результирующие значения в аргументы arg1, arg2 и т.д. Эти аргументы должны быть указателями. В результате обращения

sscanf(name, "temp%d", &n);

переменная n получает значение строки цифр, следующих за temp в name.

Упражнение 7-2

Перепишите настольный калькулятор из "лекции №4" , используя для ввода и преобразования чисел scanf и/или sscanf.

7.6. Доступ к файлам

Все до сих пор написанные программы читали из стандартного ввода и писали в стандартный вывод, относительно которых мы предполагали, что они магическим образом предоставлены программе местной операционной системой.

Следующим шагом в вопросе ввода-вывода является написание программы, работающей с файлом, который не связан заранее с программой. Одной из программ, которая явно демонстрирует потребность в таких операциях, является cat, которая объединяет набор из нескольких именованных файлов в стандартный вывод. Программа cat используется для вывода файлов на терминал и в качестве универсального сборщика ввода для программ, которые не имеют возможности обращаться к файлам по имени. Например, команда

cat x.c,y.c

печатает содержимое файлов x.c и y.c в стандартный вывод.

Вопрос состоит в том, как организовать чтение из именованных файлов, т.е., как связать внешние имена, которыми мыслит пользователь, с фактически читающими данные операторами.

Эти правила просты. Прежде чем можно считывать из некоторого файла или записывать в него, этот файл должен быть открыт с помощью функции fopen из стандартной библиотеки. функция fopen берет внешнее имя (подобное x.c или y.c ), проводит некоторые обслуживающие действия и переговоры с операционной системой (детали которых не должны нас касаться) и возвращает внутреннее имя, которое должно использоваться при последующих чтениях из файла или записях в него.

Это внутреннее имя, называемое " указателем файла", фактически является указателем структуры, которая содержит информацию о файле, такую как место размещения буфера, текущая позиция символа в буфере, происходит ли чтение из файла или запись в него и тому подобное. Пользователи не обязаны знать эти детали, потому что среди определений для стандартного ввода-вывода, получаемых из файла stdio.h, содержится определение структуры с именем file. Единственное необходимое для указателя файла описание демонстрируется примером:

file *fopen(), *fp;

Здесь говорится, что fp является указателем на file и fopen возвращает указатель на file. Oбратите внимание, что file является именем типа, подобным int, а не ярлыку структуры ; это реализовано как typedef. (Подробности того, как все это работает на системе UNIX, приведены в "лекции №8" ).

Фактическое обращение к функции fopen в программе имеет вид:

fp=fopen(name,mode);

Первым аргументом функции fopen является "имя" файла, которое задается в виде символьной строки. Второй аргумент mode("режим") также является символьной строкой, которая указывает, как этот файл будет использоваться. Допустимыми режимами являются: чтение (" r "), запись (" w ") и добавление (" a ").

Если вы откроете файл, который еще не существует, для записи или добавления, то такой файл будет создан (если это возможно).

Открытие существующего файла на запись приводит к отбрасыванию его старого содержимого. Попытка чтения несуществующего файла является ошибкой. Ошибки могут быть обусловлены и другими причинами (например, попыткой чтения из файла, не имея на то разрешения). При наличии какой-либо ошибки функция возвращает нулевое значение указателя null (которое для удобства также определяется в файле stdio.h ).

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

c=getc(fp)

помещает в "C" следующий символ из файла, указанного посредством fp, и EOF, если достигнут конец файла.

функция putc, являющаяся обращением к функции getc,

putc(c,fp)

помещает символ " c " в файл fp и возвращает " c ". Подобно функциям getchar и putchar, getc и putc могут быть макросами, а не функциями.

При запуске программы автоматически открываются три файла, которые снабжены определеными указателями файлов. Этими файлами являются стандартный ввод, стандартный вывод и стандартный вывод ошибок; соответствующие указатели файлов называются stdin, stdout и stderr. Обычно все эти указатели связаны с терминалом, но stdin и stdout могут быть перенаправлены на файлы или в поток ( pipe ), как описывалось в разделе 7.2. "лекции №7" .

Функции getchar и putchar могут быть определены в терминалах getc, putc, stdin и stdout следующим образом: #define getchar() getc(stdin) #define putchar(c) putc(c, stdout). При работе с файлами для форматного ввода и вывода можно использовать функции fscanf и fprintf. Они идентичны функциям scanf и printf, за исключением того, что первым аргументом является указатель файла, определяющий тот файл, который будет читаться или куда будет вестись запись; управляющая строка будет вторым аргументом.

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

#include <stdio.h>
  main(argc, argv)   /*cat: concatenate files*/
  int argc;
  char *argv[];
  {
 file *fp, *fopen();
 if(argc==1) /*no args; copy standard input*/
 filecopy(stdin);
 else
 while (--argc > 0)
      if ((fp=fopen(*++argv,"r"))==null) {
         printf("cat:can't open %\n",*argv);
         break;
      } else {
         filecopy(fp);
         fclose(fp);
      }
  }
   filecopy(fp)  /*copy file fp to standard output*/
   file *fp;
   {
  int c;
  while ((c=getc(fp)) !=EOF)
  putc(c, stdout);
   }

указатели файлов stdin и stdout заранее определены в библиотеке ввода-вывода как стандартный ввод и стандартный вывод ; они могут быть использованы в любом месте, где можно использовать объект типа file*. Они однако являются константами, а не переменными, так что не пытайтесь им что-либо присваивать.

функция fclose является обратной по отношению к fopen ; она разрывает связь между указателем файла и внешним именем, установленную функцией fopen, и высвобождает указатель файла для другого файла. Большинство операционных систем имеют некоторые ограничения на число одновременно открытых файлов, которыми может распоряжаться программа. Поэтому, то как мы поступили в cat, освободив не нужные нам более объекты, является хорошей идеей. Имеется и другая причина для применения функции fclose к выходному файлу - она вызывает выдачу информации из буфера, в котором putc собирает вывод. (При нормальном завершении работы программы функция fclose вызывается автоматически для каждого открытого файла).

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >
Ярослав Воробей
Ярослав Воробей
Россия
Дмитрий Левин
Дмитрий Левин
Россия