КаталогИндекс раздела
НазадОглавление


Лабораторная работа ╧16
Прямой файловый ввод-вывод

1. Цель работы

Целью лабораторной работы является получение практических навыков в работе с функциями прямого файлового ввода-вывода.

2. Темы для предварительной проработки

3. Задания для выполнения

Выполнить задание лабораторной работы ╧14 с условием, что данные, которые обрабатываются программой, находятся в файле, а в оперативной памяти одновременно расположен только одна запись файла.

4. Варианты индивидуальных заданий

 1        2        3        4        5        6        7        8        9       10      
11       12       13       14       15       16       17       18       19       20      
21       22       23       24       25       26       27       28       29       30      

5. Пример решения задачи (вариант 30)

Решение представляется с ссылками на работы ╧╧ 2, 10, 14.

5.1. Определение основных переменных программы

5.2. Определение стректуры программы

Как и в работе ╧10, используем ранее разработанную структуру:

  struct mon { . . . }; 
  

В этой работе она будет представлять запись файла на внешней памяти. Файл работы ╧14 L14.H мы можем перенести в этот проект без изменений.

В данной работе мы предлагаем немного другую структуру работы модулей программы с данными. Если в работе ╧14 масив, который мы обрабатывали, был глобальным, т.е. доступ к нему можно было обеспечить из любых модулей программы, то здесь мы используем, так называемый, подход "спрятанной базы данных" - сделаем файл доступным только из одного модуля, а все остальные модули будут работать с данными файла через функции этого модуля. Нельзя сказать, что какой-то подход абсолютно лучший, а какой-то - худший. Можно было бы решить задачу работы ╧14 со "спрятанной базой данных", или текущую задачу - с глобальной файловой переменной.

5.3. Разработка програмных модулей

5.3.1. Структура программы

Согласно условию, отдельный модуль должна составлять главная функция. Еще один модуль - функции, которые обеспечивают работу с файлом. Эти функции заменяют функции модуля L14-1.C из работы ╧14. Наконец, пересмотрев проект работы ╧14, мы приходим к выводу, что все функции, которые определены в файле L14-2.C, нам пригодятся и в данном проекте, следовательно файлы L14-2.C и L14-2.H переносятся в данный проэкт без изменений.

5.3.2. Модуль L16.C

В этом модуле определяется функция main(). Ее алгоритм и текст в основном совпадают с алгоритмом и текстом главной функции работы ╧14. отмечаем только некоторые расхождения.

Имя файла данных в этой работе передается функции main() как параметр, значит функция имеет параметры и начинается с проверки параметра (как в работе ╧15).

Файл данных следует открыть в начале работы программы и закрыть в конце - это выполняется обращением к функциям initf() и commit() соответственно.

В функции main() есть локальная переменная программы x, в которой хранится одна структура-монастырь. При добавлении новой записи данные вводятся функцией ent_data() в эту переменную, а потом передаются функции f_add() для записи в файл.

5.3.3. Модуль L16-1.C

В этом модуле сосредоточены функции, которые имеют доступ к файлу данных. Вне блоков в этом файле объявлена файловая переменная:

static int file;
Эта переменная доступна для всех функций данного файла, но не доступна вне файла.

В начало текста включаются также библиотечные файлы stdio.h, io.h (описание функций прямого файлового ввода-вывода), fcntl.h и sys\stat.h (макроконстанты прямого файлового ввода-вывода), stdlib.h (описание функции exit()) и собственные файлы L14.H (описание структуры данных) и L14-2.H (описания функций пользователя, которые находятся в файле L14-2.C).

5.3.3.1. Функция initf().
Функция открывает файл данных. Ее параметр - указатель на символьную строку - имя файла. Функция не возвращает значений.

Выполнение функции начинается с проверки наличия файла с помощью библиотечной функции access(). Если access() возвращает -1, файл не существует, тогда он создается с помощью библиотечной функции creat(). Созданный файл автоматически открывается на чтение/запись, но по умолчанию при этом устанавливается текстовый режим передачи данных (O_TEXT). Для установления двоичного режима передачи в системную переменную _fmode явным образом записывается значения O_BINARY.

Если access() возвращает 0, файл существует, тогда он открывается функцией open() на чтение/запись в двоичном режиме.

Значение, которое возвращает функция creat() или open(), записывается в общую для модуля переменную file, через которую обращаются к открытому файлу другие функции модуля.

И при создании, и при открытии проверяется результат выполнения библиотечной функции и, если он неудовлетворительный (возвращается отрицательное значение), выводится сообщение и программа заканчивается аварийно.

5.3.3.2. Функция commit().
Функция выполняет закрытие файла. Функция не имеет параметров и не возвращает никакого значения. Ее код состоит лишь из вызова библиотечной функции close().

5.3.3.3. Функция f_add().
Функция добавляет новую запись в конец файла. Параметр функции - указатель на структуру (MON *), в которой находятся данные записи. Функция не возвращает значения.

Функция устанавливает файловый курсор в конец файла lseek() и записывает в файл данные - write().

5.3.3.4. Функция fcheck_number().
Ее параметр - номер записи, возвращает 0 или -1. Функция выполняет то же самое действие, что и функция check_number() в работе ╧14. Но если там максимальный номер записи сохранялся в переменной программы, тут вон определяется из размера файла. Для этого файловый курсор устанавливается в конец файла, значение, которое при этом возвращает lseek() - размер файла в байтах. Поделивши его на размер одной записи, получаем количество записей в файле.

5.3.3.5. Функция fshow_1().
Функция выполняет чтение из файла и вывод на экран данных одной записи с заданным номером. Параметр функции - номер записи, значение не возвращает.

Функция вычисляет смещение необходимой записи от начала файла в байтах, устанавливает файловый курсор на это место и читает данные в свою локальную переменную х. Для вывода на экран используется функция show_1(), унаследованная из работы ╧14.

5.3.3.6. Функция fshow_all().
Функция выполняет чтение из файла и вывод на экран данных всего файла в виде таблици. Параметров не имеет, значение не возвращает.

Функция устанавливает файловый курсор на начало файла, выводит (print_head()) заголовок таблици, потом в цикле читает (read()) следующую запись из файла и выводит его на экран (show_row()). Когда будет достигнут конец файла, в следующей итерации цикла read() возвращает 0, что приведет к выходу из цикла.

5.3.3.7. Функция fdel_item().
Функция извлекает из файла запись с заданным номером. Параметр функции - номер записи, значения не возвращает.

Алгоритм извлечения - такой же, как и в случае с массивом в работе ╧14, но способы выполнения этого алгоритма на файле совсем другие.

Сначала вычисляется смещение в файле записи, следующей за заданной и файловый курсор устанавливается на нее. Далее организуется цикл.

В каждой итерации цикла считывается запись, на которую установлен курсор. Потом курсор смещается назад - на запись, предшествующую считанной и считанные данные записываются туда. Курсор смещается вперед еще на одину запись, эта запись будет читаться в следующей итерации цикла.

Цикл повторяется, пока не будет достигнут конец файла. Таким образом, все записи, которые размещены за заданной, смещаются на одно место ближе к началу файла.

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

5.4. Создание программного проекта

Техника создания программного проекта - такая же, как и в работе ╧14. В состав проекта включаются модули L16, L16-1, L14-2.

5.5. Текст программы

/*******************************************************/
/*               Лабораторная работа ╧16               */
/*              Прямой файловый ввод-вывод             */
/*            Пример выполнения. Вариант ╧30.          */
/*******************************************************/
/*  Файлы L14.H, L14-2.H, L-14-2.C - см.работу ╧14     */
/*******************************************************/

/*******************************************************/
/*               Лабораторная работа ╧16               */
/*                    Файл L16-1.H                     */
/*******************************************************/
/* Описания функций файла L16-1.C */
void initf(char *f);
void commit(void);
void f_add(MON *a);
int fcheck_number(int);
void fshow_1(int);
void fshow_all(void);
void fdel_item(int);

/*******************************************************/
/*               Лабораторная работа ╧16               */
/*                     Файл L16.C                      */
/*******************************************************/
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include "l14.h"
#include "l14-2.h"
#include "l16-1.h"

/**** главная функция ****/
int main(int an, char *av[]) {
 MON x;
 int op;    /* операция */
 int num;   /* номер элемента */
 char eoj;  /* признак конця */
  /* проверка параметра */
  if (an<2) {
    printf("Неправильный вызов програмы\n");
    exit(0);
    }
  /* открытие файла */
  initf(av[1]);
  for (eoj=0; !eoj; ) {
    /* вивод меню */
    printf("1 - Добавить элемент\n");
    printf("2 - Удалить элемент\n");
    printf("3 - Показать элемент по номеру\n");
    printf("4 - Показать все\n");
    printf("0 - Виход\n");
    printf("Вводите >");
    /* вибор из меню  */
    scanf("%d",&op);
    switch(op) {
      case 0:
        eoj=1;
        break;
      case 1: /* добавить */
        if (ent_data(&x)>=0)
           f_add(&x);
        break;
      case 2:  /* удалить */
        if (!fcheck_number(num=get_number()))
          fdel_item(num);
        break;
      case 3:  /* показать один */
        if (!fcheck_number(num=get_number()))
          fshow_1(num);
        break;
      case 4: /* показать все */
        fshow_all();
        break;
      default:
        printf("Неправильная операция\n");
        break;
      }
    if (op) {
      printf("Нажмите любую клавишу\n");
      getch();
      }  /* if */
    }  /* for */
  /* закрытие файла */
  commit();
  return 0;
}  /* main */

/*******************************************************/
/*               Лабораторная работа ╧16               */
/*                    Файл L16-1.C                     */
/*******************************************************/
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <stdlib.h>
#include "l14.h"
#include "l14-2.h"

static int file;  /* файл-таблица */

/**** открытие или создание файла ****/
void initf(char *fname) {
  if (access(fname,0)<0) {
    /* несуществующий файл - создать */
    _fmode=O_BINARY;
    if ((file=creat(fname,S_IREAD|S_IWRITE))<0) {
      printf("Can't create file %s\n",fname);
      exit(0);
      }
    }
  else /* существующий файл - открыть */
    if ((file=open(fname,O_RDWR|O_BINARY))<0) {
      printf("Can't open file %s\n",fname);
      exit(0);
      }
}

/**** закрытие файла ***/
void commit() {
  close(file);
}

/**** добавление в конец файла ****/
void f_add(MON *a) {
  lseek(file,0,SEEK_END);
  write(file,a,SMON);
}

/**** проверка номера записи ****/
int fcheck_number(int n) {
 long nn;
  if (n<1) {
    printf("Минимальный номер : 1\n");
    return -1;
    }
  nn=lseek(file,0,SEEK_END)/SMON;
  if (n>nn) {
    printf("Максимальный номер :%d\n",(int)nn);
    return -1;
    }
  return 0;
}

/**** вивод одной записи ****/
void fshow_1(int n) {
 long t;
 MON x;
  t=n-1; t*=SMON;
  lseek(file,t,SEEK_SET);
  read(file,&x,SMON);
  show_1(&x);
}

/**** вивод всех записей ****/
void fshow_all() {
 MON x;
  lseek(file,0,SEEK_SET);
  print_head();
  while(read(file,&x,SMON))
    show_row(&x);
  print_line();
}

/**** удаление записи ****/
void fdel_item(int n) {
 long t;
 MON x;
  t=n; t*=SMON;
  lseek(file,t,SEEK_SET);
  while (read(file,&x,SMON)) {
    t=lseek(file,t-SMON,SEEK_SET);
    write(file,&x,SMON);
    t=tell(file);
    t=lseek(file,SMON,SEEK_CUR);
    }
  chsize(file,t-SMON);
}

5.4. Отладка программы

Методика отладки программы - такая же, как и в работе ╧14.

5.5. Результаты работы программы

Программа работает в интерактивном режиме, значит, объем информации, которой обменивается программа и оператор, достаточно большой. Поэтому мы не приводим образцы выполнения программы. В правильном выполнении программы можно убедится наглядно.

5.6. Выводы

При выполнении лабораторной работы изучены вопросы:


КаталогИндекс раздела
НазадОглавление