Advertisement
zhukov000

Lab 9

Apr 7th, 2020
301
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 15.16 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. #include <locale.h>
  5.  
  6. // файл DATAFILE => загрузка данных в data => обработка данных в data => записывается в файл DATAFILE
  7. #define DATAFILE "auto.db"
  8.  
  9. // данные об автомобилях - одна строка БД
  10. typedef struct
  11. {
  12.   char fio[100];
  13.   char address[200];
  14.   char phone[15];
  15.   char mark[25];
  16.   char year[5];
  17.   char number[12];
  18.   char color[25];
  19. } row_auto;
  20. // row_auto - это тип, для хранения информации об одной записи о автомобиле
  21.  
  22. row_auto *data;     // массив данных
  23. row_auto filter;    // условия для выбора данных (фильтрация)
  24. row_auto sortorder; // правила сортировки
  25.  
  26. int cnt; // количество записей в массиве data
  27.  
  28. // функция выполняет соединение с БД, т.е. открывает файл
  29. // возвращает дескриптор файла
  30. // created - приравнивается 1, если файл был создан
  31. // default_mode - режим открытия файла по умолчанию
  32. FILE * connect(const char * path, int * created, const char * default_mode)
  33. {
  34.   FILE * fp = fopen(path, default_mode);
  35.   if ( fp == 0 ) // если не получилось открыть файл в режиме default_mode
  36.   {
  37.     fp = fopen(path, "w");  // файла нет - попробовать создать новый
  38.     assert(fp != 0);        // если fp == 0, то ошибка
  39.     printf("File DB %s not found and was create\n", DATAFILE);
  40.     *created = 1;
  41.   }
  42.   return fp;
  43. }
  44.  
  45. // отключиться от БД
  46. void disconnect(FILE * fp)
  47. {
  48.   if ( fp )
  49.     fclose(fp); // закрыть файл, если он был открыт
  50. }
  51.  
  52. // прочитать из файла символы до разделителя (sep)
  53. void read_until_separate(FILE * f, char * str, char sep)
  54. {
  55.   char c;
  56.   int i = 0;
  57.   while ( (c = fgetc(f)) != sep )
  58.   {
  59.     str[i] = c;
  60.     ++i;
  61.   }
  62. }
  63.  
  64. // очистка фильтра
  65. // фильтр - это набор условия для отображаемых полей
  66. // например, можно отфильтровать данные по адресу, по ФИО, по марке и т.д.
  67. void filter_reset()
  68. {
  69.   filter.address[0] = filter.color[0] = filter.fio[0] = filter.mark[0] = filter.number[0] = filter.phone[0] = filter.year[0] = 0;
  70. }
  71.  
  72. // проверка подходит ли по фильтру
  73. // запись с индексом i => data[i]
  74. int check_filter(int i)
  75. {
  76.   // filter.address[0] == 0 тттк фильтр адреса не исползуется
  77.   // filter.fio = "Иванов" - выбрать только людей с фамилией Иванов
  78.   if ( filter.address[0] && strcmp(data[i].address, filter.address) ) return 0;
  79.   if ( filter.color[0] && strcmp(data[i].color, filter.color) ) return 0;
  80.   if ( filter.fio[0] && strcmp(data[i].fio, filter.fio) ) return 0;
  81.   if ( filter.mark[0] && strcmp(data[i].mark, filter.mark) ) return 0;
  82.   if ( filter.number[0] && strcmp(data[i].number, filter.number) ) return 0;
  83.   if ( filter.phone[0] && strcmp(data[i].phone, filter.phone) ) return 0;
  84.   if ( filter.year[0] && strcmp(data[i].year, filter.year) ) return 0;
  85.   return 1; // <= сюда попадем, если имеется фильтр и он не равен данным в соответствующей записи
  86. }
  87.  
  88. // массив данных в виде массива структур передается в качестве параметра
  89. void load_from_file()
  90. {
  91.   int created = 0;
  92.   cnt = 0;
  93.   FILE *fp = connect(DATAFILE, &created, "r"); // пробуем открыть файл БД на чтение
  94.   if ( !created )
  95.   { // если получилось открыть файл на чтение -> в нем есть данные
  96.     if ( fscanf(fp, "%d\n", &cnt) ) // если удалось прочитать размер
  97.     {
  98.       data = calloc(cnt, sizeof(row_auto)); // выделение памяти под cnt записей
  99.       for(int i=0; i < cnt; ++i)
  100.       {
  101.         read_until_separate(fp, data[i].fio, ';');
  102.         read_until_separate(fp, data[i].address, ';');
  103.         read_until_separate(fp, data[i].phone, ';');
  104.         read_until_separate(fp, data[i].mark, ';');
  105.         read_until_separate(fp, data[i].year, ';');
  106.         read_until_separate(fp, data[i].number, ';');
  107.         read_until_separate(fp, data[i].color, '\n');
  108.       }
  109.     }
  110.   }
  111.   disconnect(fp);
  112.   printf("Data base was load, count rows = %d\n", cnt);
  113. }
  114.  
  115. // заменяем в строке точку с запятой на запятую
  116. void fix_semicolon(char * str)
  117. {
  118.   for( int i = 0; str[i] != 0; ++i)
  119.     if ( str[i] == ';' )
  120.       str[i] = ',';
  121. }
  122.  
  123. // сохранение данных в файл-БД
  124. void save_data_to_file()
  125. {
  126.   int created = 0;
  127.   FILE *fp = connect(DATAFILE, &created, "w"); // открываем сразу на запись
  128.   fprintf(fp, "%d\n", cnt);
  129.   for(int i=0; i < cnt; ++i)
  130.   {
  131.     fix_semicolon(data[i].fio);
  132.     fix_semicolon(data[i].address);
  133.     fix_semicolon(data[i].phone);
  134.     fix_semicolon(data[i].mark);
  135.     fix_semicolon(data[i].year);
  136.     fix_semicolon(data[i].number);
  137.     fix_semicolon(data[i].color);
  138.     // записываем запись об автомобиле в файл
  139.     fprintf(fp, "%s;%s;%s;%s;%s;%s;%s\n", data[i].fio, data[i].address, data[i].phone, data[i].mark, data[i].year, data[i].number, data[i].color);
  140.   }
  141.   disconnect(fp);
  142. }
  143.  
  144. // ввод одной записи (строки БД)
  145. // gets - считать строку (возможно с пробелами)
  146. void input_row(row_auto * row)
  147. {
  148.   char empty_str[1];
  149.   fflush(stdin); // очистить поток ввода от символов
  150.   gets( empty_str ); // пропуск первого вызова gets
  151.   printf("ФИО (не более 100 символов): ");
  152.   gets( row->fio );
  153.  
  154.   printf("Адрес (не более 200 символов): ");
  155.   gets( row->address );
  156.  
  157.   printf("Телефон (не более 15 символов): ");
  158.   gets( row->phone );
  159.  
  160.   printf("Марка (не более 25 символов): ");
  161.   gets( row->mark );
  162.  
  163.   printf("Гос.Номер (не более 12 символов): ");
  164.   gets( row->number );
  165.  
  166.   printf("Цвет (не более 25 символов): ");
  167.   gets( row->color );
  168.  
  169.   printf("Год (не более 4 символов): ");
  170.   gets( row->year );
  171. }
  172.  
  173. // добавление строки в БД
  174. void add_row()
  175. {
  176.   printf("Ввод данных:\n");
  177.   cnt++; // увеличиваем количество данных
  178.   data = realloc(data, cnt * sizeof(row_auto)); // изменить объем выделенной памяти
  179.   input_row(&data[cnt-1]); // вводим данные в последнюю (новую) строку
  180. }
  181.  
  182. // выбор строки для действия
  183. int select_row()
  184. {
  185.   if (cnt > 0)
  186.   {
  187.     int k;
  188.     printf("Введите номер строки [1..%d]:", cnt);
  189.     scanf("%d", &k);
  190.     if (k >= 1 && k <= cnt)
  191.       return k;
  192.   }
  193.   return 0;
  194. }
  195.  
  196. // удаление строки из БД
  197. void del_row()
  198. {
  199.   if (cnt > 0)
  200.   {
  201.     printf("Удаление записи\n");
  202.     int k = select_row();
  203.     if (!k)
  204.       printf("Некорректный номер записи\n");
  205.     else
  206.     {
  207.       if (k < cnt)
  208.       { // последнюю запись копируем на место удаляемой
  209.         // strcpy(dst, src) - скопировать из src в dst
  210.         strcpy(data[k-1].address , data[cnt-1].address);
  211.         strcpy(data[k-1].color , data[cnt-1].color);
  212.         strcpy(data[k-1].fio , data[cnt-1].fio);
  213.         strcpy(data[k-1].mark , data[cnt-1].mark);
  214.         strcpy(data[k-1].number , data[cnt-1].number);
  215.         strcpy(data[k-1].phone , data[cnt-1].phone);
  216.         strcpy(data[k-1].year , data[cnt-1].year);
  217.       }
  218.       cnt--;
  219.     }
  220.   }
  221.   else
  222.     printf("Нечего удалять - база пуста\n");
  223. }
  224.  
  225. // отобразить записи в БД
  226. void show_rows()
  227. {
  228.   fflush(stdin);
  229.   printf("\n--------------------------------------------------------------------------------\n");
  230.   for(int i=0; i<cnt; ++i)
  231.   {
  232.     if ( !check_filter(i) ) continue;
  233.     printf("\n-Запись # %2d--------------------------------------------------------------------\n", i + 1);
  234.     printf("ФИО: %s\n", data[i].fio);
  235.     printf("Адрес: %s\n", data[i].address);
  236.     printf("Телефон: %s\n", data[i].phone);
  237.     printf("Марка: %s\n", data[i].mark);
  238.     printf("Год: %s\n", data[i].year);
  239.     printf("Гос.Номер: %s\n", data[i].number);
  240.     printf("Цвет: %s\n", data[i].color);
  241.  
  242.     printf("\n----Нажмите Enter для продолжения-----------------------------------------------\n");
  243.     getchar();
  244.   }
  245. }
  246.  
  247. // поиск строки
  248. void find_rows()
  249. {
  250.   printf("Введите параметры поиска:\n!!!Если поле не участвует в поиске - оставьте значение пустым!!!\n");
  251.   input_row(&filter); // ввод фильтра для поиска
  252.   show_rows();
  253.   filter_reset(); // отмена фильтра
  254. }
  255.  
  256. // редактирование строки
  257. void edit_row()
  258. {
  259.   if (cnt > 0)
  260.   {
  261.     printf("Редактирование записи\n");
  262.     int k = select_row();
  263.     if (!k)
  264.       printf("Некорректный номер записи\n");
  265.     else
  266.       input_row(&data[k-1]);
  267.   }
  268.   else
  269.     printf("Нечего редактировать - база пуста\n");
  270. }
  271.  
  272. // вернуть 1, если запись a меньше записи b, в противном случае - 0
  273. // sortorder.fio = '1' - сортировка по возрастанию ФИО
  274. // sortorder.fio = '2' - сортировка по убыванию ФИО
  275. int row_less(const row_auto * a, const row_auto * b)
  276. {
  277.   if ( (sortorder.fio[0] == '1' && strcmp(a->fio, b->fio) < 0) || (sortorder.fio[0] == '2' && strcmp(a->fio, b->fio) > 0) ) return 1;
  278.   if ( (sortorder.address[0] == '1' && strcmp(a->address, b->address) < 0) || (sortorder.address[0] == '2' && strcmp(a->address, b->address) > 0) ) return 1;
  279.   if ( (sortorder.phone[0] == '1' && strcmp(a->phone, b->phone) < 0) || (sortorder.phone[0] == '2' && strcmp(a->phone, b->phone) > 0) ) return 1;
  280.   if ( (sortorder.mark[0] == '1' && strcmp(a->mark, b->mark) < 0) || (sortorder.mark[0] == '2' && strcmp(a->mark, b->mark) > 0) ) return 1;
  281.   if ( (sortorder.year[0] == '1' && strcmp(a->year, b->year) < 0) || (sortorder.year[0] == '2' && strcmp(a->year, b->year) > 0) ) return 1;
  282.   if ( (sortorder.number[0] == '1' && strcmp(a->number, b->number) < 0) || (sortorder.number[0] == '2' && strcmp(a->number, b->number) > 0) ) return 1;
  283.   if ( (sortorder.color[0] == '1' && strcmp(a->color, b->color) < 0) || (sortorder.color[0] == '2' && strcmp(a->color, b->color) > 0) ) return 1;
  284.   return 0;
  285. }
  286.  
  287. // обмен значений
  288. void swap(row_auto * a, row_auto* b)
  289. {
  290.   row_auto t = *a;
  291.   *a = *b;
  292.   *b = t;
  293. }
  294.  
  295. // поиск индекса для деления массива - индекса стержневого элемента:
  296. // слева от этого индекса будут элементы меньше его, справа - больше
  297. int partition (row_auto * a, int left, int right)
  298. {
  299.     row_auto * pivot = &a[right - 1]; // стержневой элемент
  300.     int i = left; // индекс минимального элемента
  301.     for (int j = left; j < right - 1; j++)
  302.     {
  303.       if ( row_less(&a[j], pivot) )
  304.       {
  305.         swap(&a[i], &a[j]);
  306.         i++;
  307.       }
  308.     }
  309.     swap(&a[i], &a[right - 1]);   // ставим стержневой элемент на верное место
  310.     return i;                     // возвращаем индекс стержневого элемента
  311. }
  312.  
  313. // сортировка массива a на отрезке [left, right)
  314. void quick_sort(row_auto * a, int left, int right)
  315. {
  316.     if (left + 1 < right)
  317.     {
  318.       // находим индекс разделения - элемент a[pi] стоит на своем месте
  319.       int pi = partition(a, left, right);
  320.       // сортируем по частям
  321.       quick_sort(a, left, pi);
  322.       quick_sort(a, pi + 1, right);
  323.     }
  324. }
  325.  
  326. // сортировка строк
  327. void sort_rows()
  328. {
  329.   printf("Выберите параметры сортировки:\n");
  330.   printf(" - оставьте поле пустым, если по нему не сортировать\n");
  331.   printf(" - введите 1 для сортировки по возрастанию\n");
  332.   printf(" - введите 2 для сортировки по убыванию\n");
  333.   printf("!! Другие значения будут проигнорированы !!\n");
  334.   input_row(&sortorder); // определяем порядок сортировки
  335.   quick_sort(data, 0, cnt); // сортировка алгоритмом быстрой сортировки
  336.   show_rows();
  337. }
  338.  
  339. // освобождаем выденную память
  340. void free_memory()
  341. {
  342.   /* for(int i=0; i<cnt; ++i)
  343.   {
  344.     free(data[i].fio);
  345.     free(data[i].address);
  346.     free(data[i].phone);
  347.     free(data[i].mark);
  348.     free(data[i].number);
  349.     free(data[i].color);
  350.   }*/
  351.   free(data);
  352. }
  353.  
  354. // функция-меню
  355. void menu()
  356. {
  357.   filter_reset(); // инициализация пустого фильтра
  358.   load_from_file(); // загрузка из файла БД
  359.   // <тип. возвращаемого зачения> (* <имя функции>[]) (<типы параметров функции>) - массив указателей на функцию
  360.   // массив указателей на функции с соответствующими действиями
  361.   void (*foo[])() = {add_row, del_row, find_rows, edit_row, sort_rows, show_rows};
  362.   while (1)
  363.   {
  364.     int code;
  365.     printf("\n######Выберите команду:######\n");
  366.     printf("# 1. Добавить строку        #\n");
  367.     printf("# 2. Удалить строку         #\n");
  368.     printf("# 3. Найти строку           #\n");
  369.     printf("# 4. Отредактировать строку #\n");
  370.     printf("# 5. Сортировка строк       #\n");
  371.     printf("# 6. Вывод строк            #\n");
  372.     printf("# 0. Выход из программы     #\n");
  373.     printf("Введите номер действия (нажмите клавишу на клавиатуре):");
  374.     code = getchar() - '0';
  375.     printf("\n");
  376.     if (!code) break;
  377.     if (code < 7 && code > 0)
  378.       foo[code-1]();
  379.     else
  380.       printf("Ошибка: неверный код действия!\n");
  381.   }
  382.   save_data_to_file(); // сохранить данные в файл
  383.   free_memory(); // освободить выделенную память
  384. }
  385.  
  386. // точка входа в программу
  387. int main()
  388. {
  389.   setlocale(LC_ALL, "Russian");
  390.   menu();
  391.   return 0;
  392. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement