#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
//данные хранятся в виде массива структур из указателя и ключа
//структура элемента таблицы
typedef struct Item
{
int key; //ключ
int release; //номер версии
int offset; //смещение в файле (по отношению к началу файла)
int len; //длина информации
char* info; //поле данных - строка
}Item;
//структура - таблица
typedef struct Table
{
int size; //максимальный размер таблицы
int count; //текущий размер таблицы
Item* val; //массив структур item
FILE* fd; //дескриптор файла, чтобы выполнять операции с файлом данных
}Table;
const int SIZE = 10; //максимальны размер таблицы
//меню
int F1_End(Table*); //функция выхода
int F2_Menu(Table*); //меню
int F3_Print(Table*); //вывод элементов
int F4_Input(Table*); //ввод элемента
int F5_DeleteKey(Table*); //удаление элементов предыдущих версий
int F6_FindKey(Table*); //поиск и копирование элемента
int F7_FindRelease(Table*); //поиск версии
//функции работы с таблицей
Table* TableCreate(int size); //функция создания пустой таблицы
void TableDelete(Table* T); //удаление таблицы
void TablePrint(Table* T); //вывод таблицы
int TableAdd(Table* T, int key, char* info); //добавление элемента с ключом в таблицу
int TableFindLast(Table* T, int key); //поиск элемнета последней версии по ключу в таблице (начинается с конца таблицы)
Table* TableFindKey(Table* T, int key); //поиск и получение копии элемента
char* TableFindRelease(Table* T, int key, int release); //поиск по ключу и версии
int TableDeleteRelease(Table* T); //удаление предыдущих версий
int TableZip(Table* T, int key, int release);
void TableSave(Table* T);
int D_load(Table* T);
int TableLoad(Table* T, char* fname);
int TableCreateFile(Table* T, int size, char* fname);
int ItemPrint(Item*); //вывод элемента таблицы
//другие функции
int getInt(int*); //чтение целого числа
int getStr(char**); //функция ввода строк
const char* msgs[] = { NULL, "Exit programm", "Show menu", "Print table",
"Input item", "Delete releases", "Find key", "Find release" }; //строки меню
int main()
{
int ex = 0; //переменые для удобного вывода меню
int menu = 0; //далее до конца - вывод меню
int(*fun[]) (Table*) = { NULL, F1_End, F2_Menu, F3_Print,
F4_Input, F5_DeleteKey, F6_FindKey, F7_FindRelease }; //массив функций
Table T = { 0,0,NULL,NULL };
if (D_load(&T) == 0) //если не удалось создать файл
return 1;
F2_Menu(&T); //вывод меню
//выбор пункта меню
while (ex != 1)
{
menu = 0; //пункт меню
while (menu <= 0 || menu >= 9)
{
printf("Enter number menu (2 for enter menu): ");
getInt(&menu); //чтение номера пункта меню
if (menu <= 0 || menu >= 8) { // обнаружен неверный номер - ошибка
printf("%s\n", "Error! Wrong symbol.");
scanf("%*c");
}
}
if (menu != 1) {
ex = (*fun[menu])(&T);
}
else {
ex = 1;
}
}
TableSave(&T);
printf("Good buy!");
TableDelete(&T);
return 0;
}
//интерфейсные функции
//завершение программы
int F1_End(Table* T)
{
TableDelete(T); //удаление таблицы
return 1;
}
//вывод меню
int F2_Menu(Table* T)
{
int i;
for (i = 1; i < 8; ++i)
printf("%d %s\n", i, msgs[i]);//вывод пунктов меню
return 0;
}
//вывод таблицы
int F3_Print(Table* T)
{
puts("Table:");
TablePrint(T); //вывод
return 0;
}
//добавление ключа
int F4_Input(Table* T)
{
int key = 0;
char* info;
int res;
printf("Enter key: ");
getInt(&key); //читаем ключ
printf("Enter information: ");
res = getStr(&info); //чтение информации
if (res) //проверка на возможность выделить память
{
puts("Memory is out");
TableDelete(T); //удаляем таблицу при нехватке памяти
return 1;
}
if (TableAdd(T, key, info)) //если добавление не успешно
puts("Table is full");
else //если успешно
puts("Item added.");
return 0;
}
//удаление элемента
int F5_DeleteKey(Table* T)
{
printf("Deleted %d items.\n", TableDeleteRelease(T)); //выводим сколько ключей мы удалили
return 0;
}
//поиск в таблице ключа
int F6_FindKey(Table* T)
{
int key = 0;
Table* NewTable;
printf("Enter key: "); //ввод ключа
getInt(&key);
NewTable = TableFindKey(T, key); //получаем указатель на ктаблицу с копиями ключа
if (NewTable) //если был хотя бы один элемент с таким ключом
{
puts("Table:");
TablePrint(NewTable); //вывод
TableDelete(NewTable); //и удаление таблицы
}
else
{
puts("Keys not found."); //или выводим сообщение об ошибке
}
return 0;
}
//поиск в таблице ключа и версии
int F7_FindRelease(Table* T)
{
int key = 0;
int release = 0;
char* info;
printf("Enter key: ");
getInt(&key); //ввод ключа
printf("Enter release: ");
getInt(&release); //ввод версии
info = TableFindRelease(T, key, release); //поиск информации по кючу и версии
if (info)
printf("Information: %s.\n", info); //вывод если нашли
else
puts("Key not found."); //иначе выводим сообщение об ошибке
return 0;
}
//функци работы с таблицей
//создание таблицы из файла
int D_load(Table* T)
{
int size;
char* fname = NULL;
printf("Enter file name:");
getStr(&fname); //ввод названия файла
//fname = "data.dat";
if (fname == NULL) //если не введено
return NULL;
if (TableLoad(T, fname) == 0) //если файла нет
{
printf("File not fount. Create file. Enter Size:");
if (getInt(&size) == 0)
return 0;
return TableCreateFile(T, size, fname); //создаем файл
}
return 1;
}
//загрузка таблицы из файла
int TableLoad(Table* T, char* fname)
{
int i;
Item z;
T->fd = fopen(fname, "r+b"); //открываем файл
if (T->fd == 0) //если файла нет
return 0;
fread(&T->size, sizeof(int), 1, T->fd); //чтение размера
T->val = (Item*)calloc(T->size, sizeof(Item)); //создание массива значений
fread(&T->count, sizeof(int), 1, T->fd); //чтение текущего размера
fseek(T->fd, 8, SEEK_SET);
fread(T->val, sizeof(Item), T->size, T->fd); //чтение элемента
for (i = 0; i < (T->size); i++)
{
char* info = (char*)calloc(T->val[i].len, sizeof(char));
fseek(T->fd, T->val[i].offset, SEEK_SET);
fread(info, sizeof(char), T->val[i].len, T->fd);
T->val[i].info = info;
}
return 1;
}
//создание таблицы в файле
int TableCreateFile(Table* T, int size, char* fname)
{
T->size = size;
T->count = 0;
T->fd = fopen(fname, "w+b"); //открытие файла
if (T->fd == 0)
{
T->val = 0;
return 0;
}
T->val = (Item*)calloc(T->size, sizeof(Item));
fwrite(&T->size, sizeof(int), 1, T->fd); //запись размера
fwrite(&T->count, sizeof(int), 1, T->fd); //запись текущего размера
fwrite(T->val, sizeof(Item), T->size, T->fd); //запись элементов
return 1;
}
//создание таблицы, возвращаем указатель на пустую таблицу
Table* TableCreate(int size)
{
Table* T;
if (size <= 0)
return NULL;
T = (Table*)malloc(sizeof(Table)); //память под структуру таблицы
if (T == NULL)
return NULL;
T->count = 0;
T->size = size;
T->val = (Item*)calloc(size, sizeof(Item)); //память под массив элементов
if (T->val == NULL)
{
free(T);
return NULL;
}
return T;
}
void TableSave(Table* T)
{
int i;
int st = 8 + sizeof(Item) * T->size;
fseek(T->fd, 4, SEEK_SET);
fwrite(&T->count, sizeof(int), 1, T->fd);
//Пересчитываем строки
for (i = 0; i < (T->count); i++)
{
T->val[i].len = strlen(T->val[i].info) + 1;
T->val[i].offset = st;
st += T->val[i].len;
}
fwrite(T->val, sizeof(Item), T->size, T->fd);
for (i = 0; i < T->count; i++)
{
fwrite(T->val[i].info, sizeof(char), T->val[i].len, T->fd);
}
fclose(T->fd);
T->fd = 0;
}
//удаление таблицы
void TableDelete(Table* T)
{
int i;
for (i = 0; i < T->count; i++)
free(T->val[i].info); //удаление информации
free(T->val); //чистка остальной памяти
//free(T);
}
//вывод таблицы
void TablePrint(Table* T)
{
int i;
printf("count = %d, size = %d.\n", T->count, T->size);
printf("%2s %7s %7s %s\n", "i", "key", "release", "string");
if (T->count == 0)
puts("Empty table");
else
{
for (i = 0; i < T->count; i++) {
//проходим по массиву
printf("%2d ", i); //выводим очередной элемент
ItemPrint(&T->val[i]);
}
}
}
//добавление ключа в таблицу
int TableAdd(Table* T, int key, char* info)
{
int ind, st;
fseek(T->fd, 0, SEEK_END);
st = ftell(T->fd);
if (T->count == T->size) //проверка на переполнение
return 1;
ind = TableFindLast(T, key); //поиск индекса последней версии ключа
//пишем новый элемент в конец таблицы
T->val[T->count].info = info;
T->val[T->count].key = key;
if (ind < 0)
T->val[T->count].release = 1; //пишем правильную версию
else
T->val[T->count].release = T->val[ind].release + 1;
T->val[T->count].offset = st;
T->val[T->count].len = strlen(info) + 1;
fwrite(info, strlen(info) + 1, 1, T->fd);
++T->count; //увеличиваем текущее количество элементов в таблице
return 0;
}
//поиск последней версии элемента в таблице
int TableFindLast(Table* T, int key)
{
int i;
for (i = T->count - 1; i >= 0; i--) //идём с конца, т.к. версии элемента в силу механизма добавления расположены по возврастанию
if (T->val[i].key == key) //поэтому самое последнее упоминание ключа бужет с самой большой версией
return i;
return -1;
}
//сжатие массива
int TableZip(Table* T, int key, int release)
{
int j = T->count; //переменная для хранения текущего размера
int i = 0;
while (i < j)
{
if (T->val[i].key == key and T->val[i].release != release) //если у текущего элемента тот же ключ, но другая версия
{
free(T->val[i].info); //удаляем строку
for (int k = i; k < j - 1; k++) { //сдвигаем все элементы в массиве на один влево
T->val[k].info = T->val[k + 1].info;
T->val[k].key = T->val[k + 1].key;
T->val[k].release = T->val[k + 1].release;
T->val[k].len = T->val[k + 1].len;
T->val[k].offset = T->val[k + 1].offset;
}
--j; //уменьшаем текущий размер на один
}
else {
i++;
}
}
int count = 0; //переменная для посчета числа удаленных элементов
for (i = j; i < T->count; i++) {
T->val[i].info = NULL; //удаление лишней информации
count++;
}
T->count = j; //запоминаем новый размер
return count;
}
//удаление старых весий элементов
int TableDeleteRelease(Table* T)
{
int i = 0;
int count = 0;
while (i < T->count) { //проходимся по всем элемнтам
int ver = TableFindLast(T, T->val[i].key); //получаем номер последней версии текущего элемента
if (T->val[ver].release == T->val[i].release) { //если текущий элемент самая новая версия
i++; //переходим к следующему элементу
}
else {
count += TableZip(T, T->val[i].key, T->val[ver].release); //удаляем все старые версии
}
}
return count;
}
//ищем элемент и создаём таблицу с найденными элементами
Table* TableFindKey(Table* T, int key)
{
Table* NewTable = TableCreate(T->size);
int i, j;
for (i = 0; i < T->count; i++)
if (T->val[i].key == key) //если элемент сущевстует и ключи совпали
{
j = NewTable->count; //пишем в конец нашей новой таблицы
NewTable->val[j].info = (char*)calloc(strlen(T->val[i].info) + 1, sizeof(char)); //копию найденного элемента
strcpy(NewTable->val[j].info, T->val[i].info);
NewTable->val[j].key = key;
NewTable->val[j].release = T->val[i].release;
++NewTable->count; //увеличиваем размер таблицы
}
NewTable->size = NewTable->count;
if (NewTable->count)
return NewTable;
else
TableDelete(NewTable);
return NULL;
}
//поиск по ключу и версии
char* TableFindRelease(Table* T, int key, int release)
{
int i;
char* info;
for (i = 0; i < T->count; i++)
if (T->val[i].key == key && T->val[i].release == release) //если совпало
{
info = (char*)calloc(strlen(T->val[i].info) + 1, sizeof(char));
strcpy(info, T->val[i].info); //копируем инормацию
return info; //и возвращаем строку
}
return NULL;
}
//вывод элемента
int ItemPrint(Item* I)
{
printf("%7d %7d %s\n", I->key, I->release, I->info);
return 0;
}
//другие функции
//чтение целого числа
int getInt(int* a)
{
int n = 0;
do
{
n = scanf("%d", a); //считаем по адресу а - значение символа. сама функция вернёт -1, если конец файла, 0 если символ не корректный
if (n <= 0) // обнаружен некорректный символ - ошибка
printf("%s\n", "Error! Wrong symbol.");
scanf("%*c");
} while (n <= 0);
return 1;
}
//получаем строку из входного потока
int getStr(char** s)
{
char buf[21]; //считываем из входного потока строку с помощью этого буфера, кусками по 20 сиволов
int n; //сюда будет записываться результат scanf
int len = 0; //сюда длина результирующей строки
*s = (char*)malloc(1); //указатель на результирующую сткроу
**s = '\0'; //ноль байт, пока строка имеет только конец строки
//scanf("%*c");
do {
n = scanf("%20[^\n]", buf); //считываем буфер
if (n < 0)
{ //если ввели конец файла (ctrl+Z), то будет -1
free(*s); //очищаем память, возвращаем пустой указатель
return -1;
}
if (n > 0) { //если буфер не пустой
len += strlen(buf); //увеличиваем результирующую длину
*s = (char*)realloc(*s, len + 1); //добавляем память
if (*s) //если память выделилась
strcat(*s, buf); //копируем строку из буфера в конец нашей строки
else
{ //если память не выделилась
free(*s); //очищаем память
return -2;
}
}
else
scanf("%*c"); //если перенос строки, то очищаем входной поток
} while (n > 0); //пока во входном потоке есть хоть один символ
return 0;
}