- •Е.А. Киселёва «программирование 2»
- •Алматы, 2014
- •Общие указания
- •1. Введение
- •2. Порядок выполнения лабораторных работ
- •2.1. Подготовка к выполнению
- •2.2. Задания для выполнения.
- •2.3. Выполнение задания
- •3. Отчет к лабораторной работе
- •4. Литература
- •Лабораторная работа № 1. Работа с массивами
- •1. Цель работы
- •2. Темы для предварительной проработки
- •3. Задание
- •4. Пример решения задачи
- •Лабораторная работа № 2. Стандартный файловый ввод- вывод
- •1. Цель работы
- •2. Темы для предварительной проработки
- •3. Задание для выполнения
- •4. Пример решения задачи
- •Лабораторная работа № 3. Прямой файловый ввод- вывод
- •Лабораторная работа № 4. Работа с матрицами
- •1. Цель работы
- •2. Темы для предварительной проработки
- •3. Задание
- •4. Пример решения задачи
- •Лабораторная работа № 5. Указатели и массивы
- •Лабораторная работа № 6. Указатели, символьные строки и функции
- •1. Цель работы
- •2. Темы для предварительной проработки
- •3. Задание
- •4. Пример решения задачи
- •Лабораторная работа № 7. Структуры и массивы структур
- •Лабораторная работа № 8. Конструирование программ из нескольких файлов
- •1. Цель работы
- •2. Темы для предварительной проработки
- •3. Задания для выполнения
- •4. Пример решения задачи
- •Задания для срсп
- •Задание для срсп №7,8. Структуры и массивы структур. Конструирование программ из нескольких файлов
- •Тестовые вопросы выносимые на итоговую аттестацию
- •Перечень основной и дополнительной литературы, в том числе на электронных носителях
Лабораторная работа № 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.h, io.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().
Функция извлекает из файла запись с заданным номером. Параметр функции - номер записи, значения не возвращает.
Алгоритм извлечения - такой же, как и в случае с массивом, но способы выполнения этого алгоритма на файле совсем другие.
Сначала вычисляется смещение в файле записи, следующей за заданной и файловый курсор устанавливается на нее. Далее организуется цикл.
В каждой итерации цикла считывается запись, на которую установлен курсор. Потом курсор смещается назад - на запись, предшествующую считанной и считанные данные записываются туда. Курсор смещается вперед еще на одину запись, эта запись будет читаться в следующей итерации цикла.
Цикл повторяется, пока не будет достигнут конец файла. Таким образом, все записи, которые размещены за заданной, смещаются на одно место ближе к началу файла.
По выходе из цикла размер файла ограничмвается записью, предыдущей по отношению к текущей позиции файлового курсора, т.е. уменьшается на одну запись.
Создание программного проекта
В состав проекта включаются модули L16, L16-1, L14-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);
}
Отладка программы
Методика отладки программы - такая же, как и в предыдущей работе.
Результаты работы программы
Программа работает в интерактивном режиме, значит, объем информации, которой обменивается программа и оператор, достаточно большой. Поэтому мы не приводим образцы выполнения программы. В правильном выполнении программы можно убедится наглядно.
