Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МетодУказ_Си2.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
241.51 Кб
Скачать

Лабораторная работа № 3. Прямой файловый ввод- вывод

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

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

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

  • Функции пользователя

  • Создание программных проектов

  • Прямой файловый ввод-вывод

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

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

4. Пример решения задачи

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

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

Используем ранее разработанную структуру:

struct mon { . . . };

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

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

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

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

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

Модуль L16.C

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

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

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

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

Модуль L16-1.C

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

static int file;

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

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

Функция initf(). 

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

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

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

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

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

Функция commit().

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

Функция f_add().

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

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

Функция fcheck_number().

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

Функция fshow_1().

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

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

Функция fshow_all().

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

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

Функция fdel_item().

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

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

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

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

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

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

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

В состав проекта включаются модули L16L16-1L14-2.

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

/*******************************************************/

/* Лабораторная работа ╧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);

}

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

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

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

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