Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
5
Добавлен:
27.11.2024
Размер:
324.35 Кб
Скачать

Реализация CRUD (Create Read Update Delete)

Программный интерфейс (API) должен выполнять четыре типа функций. Он должен иметь

возможность создавать новые данные, а также читать, обновлять и удалять существующие.

CRUD — аббревиатура от английских слов создание (англ. create), чтение (read), модификация (update), удаление (delete). Обозначает четыре базовые функции, используемые при работе с базами данных. Такой подход может быть распространён на любые хранимые вычислительные сущности (файлы, структуры в памяти, объекты)

Create - создание или добавление новых записей;

Read - чтение, извлечение, поиск или просмотр существующих записей; Update - обновление или редактирование существующих записей; Delete - удаление, деактивация или удаление существующих записей.

Порядок выполнения работы

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

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

1. Для описания сущности book создадим структуру и перечислим атрибуты сущности в виде полей структуры:

struct book

{

int book_code;

char book_autor[30]; char book_name[50];

};

Если для решения задачи требуется описать несколько сущностей, то каждую из них следует описать таким же образом.

2. Для описания узла списка создадим структуру book_node, в которой в качестве полей запишем указатели на предыдущий и последующий узел, а также на структуру, описывающую текущий узел:

struct book_node

{

struct book_node* prev; struct book* current; struct book_node* next;

};

Описание одного узла уже достаточно для реализации динамической структуры. Но для удобства работы со списком лучше описать весь список, задав структуру book_list, в которой будут храниться указатели на первый и последний элемент списка. Также можно выделить количество элементов структуры.

struct book_list

{

struct book_node* firstNode; int count;

struct book_node* lastNode;

};

3. Создадим функцию добавления элементов в список. В качестве формальных параметров в заголовке функции запишем указатели на переменную типа book_list и назовем ее bookList, и переменную типа book и назовем ее bookRecord:

void add(book_list* bookList, book* bookRecord)

{

book_node* bookNode = (book_node*)malloc(sizeof(book_node)); if (bookList->count != 0)

{

bookNode->prev = bookList->lastNode; bookNode->current = bookRecord; bookNode->next = NULL; bookList->lastNode->next = bookNode; bookList->lastNode = bookNode; bookList->count++;

}

else

{

bookNode->prev = NULL; bookNode->current = bookRecord; bookNode->next = NULL; bookList->firstNode = bookNode; bookList->lastNode = bookNode; bookList->count++;

}

return;

}

В функции следует выделить память под переменную типа bookNode и проверить в условной конструкции: есть ли в списке какие либо элементы. Если список не пустой, то есть поле count списка book_list не равно нулю, то происходит добавление элемента в конец списка путем переопределения соответствующих указателей. В противном случае считаем, что список пустой и добавляем первый элемент в список.

4. Вызов функции следует организовать в соответствующей части программы (после авторизации).

При этом необходимо организовать ввод значений атрибутов сущностей пользователем с клавиатуры. Например, это можно сделать так:

case 1:

{

system("cls");

printf("\nn1. Добавить запись\n"); system("cls");

book* bookRecord = (book*)malloc(sizeof(book)); printf("Код книги: ");

scanf("%d", &bookRecord->book_code); getchar();

printf("Автор: ");

scanf("%[^\n]s", bookRecord->book_autor); getchar();

printf("Название: ");

scanf("%[^\n]s", bookRecord->book_name); add(&bookList, bookRecord);

getchar(); break;

}

При вызове функции в качестве фактических параметров следует передать переменные &bookList и bookRecord. Переменную bookList следует задать в начале функции main:

setlocale(LC_ALL, "Russian"); SetConsoleCP(1251); SetConsoleOutputCP(1251);

int choice = 0; char c;

book_list bookList; bookList.count = 0;

5. Для реализации функции удаления следует предварительно записать функцию поиска элемента в списке по ключу. Например можно найти и удалить из списка книгу по ее номеру.

Запишем функцию поиска:

book_node* find(book_list* bList, int book_field,...)

{

book_node* temp = bList->firstNode;

 

switch(book_field)

 

{

 

case BOOK_CODE_FIELD:

 

{

 

int* code_ptr = &book_field;

// получение адреса последнего

 

//определённого параметра

int code = *(++code_ptr);

//смещение адреса с одновременным

 

//разименовыванием

for (int i = 0; i < bList->count; i++)

{

if(temp->current->book_code == code)

{

return temp;

}

temp=temp->next;

}

break;

}

}

return 0;

}

Здесь использована именованная BOOK_CODE_FIELD, которая предварительно задается с помощью директивы препроцессора define:

#define BOOK_CODE_FIELD 1 #define BOOK_AUTHOR_FIELD 2 #define BOOK_NAME_FIELD 3

В заголовке функции в качестве формальных параметров передаются указатель на book_list и целочисленное поле в качестве ключа. Поскольку в качестве критерия поиска могут быть использованы как числовые поля, так и символьные массивы (строки), чего нельзя определить на этапе компиляции. Можно использовать передачу в функцию неопределённого числа параметров, что обозначается многоточием (...) в прототипе функции. Для доступа к неопределенному параметру используются элементы арифметики указателей - необходимо сместиться от последнего известного параметра на количество байт соответствующим его размеру.

6. Запишем функцию удаления элемента из списка:

void delete_book (book_list* bList, book_node* bookNode)

{

if (bookNode->prev==NULL)

{

(bookNode->next)->prev = NULL; bList->firstNode = bookNode->next;

}

else if (bookNode->next==NULL)

{

(bookNode->prev)->next=NULL; bList->lastNode = bookNode->prev;

}

else

{

(bookNode->next)->prev=bookNode->prev; (bookNode->prev)->next=bookNode->next;

}

bList->count--; free(bookNode->current); free(bookNode);

return;

}

В функцию передаётся указатель на узел списка, который следует удалить. При этом возможны три варианта: быть первым, последним или находиться в середине списка. Сама процедура удаления состоит в перестройке указателей на соседние узлы в списке и освобождении в памяти выделенной для удаляемого узла и его сущностей.

7. Вызов функции delete_book следует задать в основной части программы в соответствующей ветке конструкции switch..case.

При этом следует организовать диалог с пользователем, например это можно сделать так:

case 3:

{

system("cls");

printf("\nn1. Удалить записи\n"); int code, counter=0;

book_node* temp;

printf("\nВведите код книги, которую требуется удалить:"); scanf("%d", &code);

while((temp=find(&bookList, BOOK_CODE_FIELD, code)) != 0)

{

delete_book(&bookList, temp); counter++;

}

printf("Удалено %d записей", counter); break;

}

8. Для демонстрации результатов работы с базой можно организовать вывод элементов списка на экран:

case 2:

{

system("cls");

printf("\nn1. Вывести записи\n"); if (bookList.count != 0)

{

system("cls");

book_node* temp = bookList.firstNode; for (int i = 0; i < bookList.count; i++)

{

printf("Код книги: %d\tАвтор: %s\tНазвание:%s\n", temp->current->book_code, temp->current->book_autor, temp->current->book_name);

temp = temp->next;

}

}

else

{

system("cls"); printf("Список пуст\n"); system("pause");

}

break;

}

9. Результат манипуляций со списком следует сохранить в источнике данных. В качестве источника можно использовать бинарный файл:

Перед сохранением данных в файле следует определиться с его форматом. Поскольку в программе использованы детерминированные данные (есть конкретная структура, которая имеет постоянный размер и набор элементов) удобнее использовать двоичный формат файла. В начале файла следует записать количество элементов списка - так как для хранения количества используется переменная типа int то она будет занимать 4 байта. Далее записываются структуры, хранящиеся в списке.

Для записи элементов списка с файл создадим функцию:

void saveToFile (book_list* bookList, char * filename)

{

FILE * fp;

int size = sizeof(struct book);

if ((fp = fopen(filename, "wb")) == NULL)

{

perror("Error occured while opening file"); return ;

}

fwrite(&bookList->count,sizeof(int),1,fp);

book_node* temp = bookList->firstNode; for (int i = 0; i < bookList->count; i++)

{

fwrite(temp->current,sizeof(book),1,fp); temp = temp->next;

}

fclose(fp);

}

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

case 4:

{

system("cls");

printf("\nn1. Главное меню\n"); saveToFile(&bookList, "booky"); break;

}

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

void loadFromFile(book_list* bookList, char * filename)

{

FILE * fp;

int n;

int size = sizeof(struct book);

if ((fp = fopen(filename, "rb")) == NULL)

{

perror("Error occured while opening file"); return ;

}

fread(&n,sizeof(int),1,fp); for(int i=0; i<n;i++)

{

struct book * bookRecord = (struct book *) malloc(size); fread(bookRecord,size,1,fp);

add(bookList,bookRecord);

}

fclose(fp); return ;

}

Вызов функции следует записать в начале основной части программы после описания переменных и перед циклом:

loadFromFile(&bookList, "booky");

Здесь при вызове функции передается сам список и имя файла, из которого должна выполняться загрузка данных в список.

11.Если для решения задачи требуется использование нескольких сущностей, то все функции

идинамический список следует продублировать.