Односвязный список и файл - C (СИ)

Узнай цену своей работы

Формулировка задачи:

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

writeToList

и

addNodeFromFile

) происходит не правильно. Если запустить программу и попробовать удалить запись (4й пункт меню), то вывод будет не правильным (выводиться должны только имена заказчиков), выведется имя первого заказчика, а дальше пойдут цифры. 1.1. Если раскомментировать

79-83

и

128-129

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

102

(Segmentation fault). 2. Удаление заказчика если файл уже создан. Если файл не создан и я запускаю программу, добавляю нового покупателя, он добавляется в список и записывается в файл (функция

addCustomer

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

remove()

и

rename()

внутри

removeFromFile()

не работают, это видно на скриншоте: 2.1. Так же есть проблема при выводе списка заказчиков перед их удалением, если я удаляю последнего заказчика и потом захожу еще раз в 4й пункт меню, то в списке выводится мусор, а на строке

169

ошибка (Segmentation fault) и тут возникает вопрос: как проверить пустой у меня список или нет? 3. Хотелось бы сделать сортировку по именам, но как это сделать когда каждое имя внутри структуры не знаю. 4. Изменение одного из параметров структуры (ну это я и сам могу сделать )

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

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
struct Customer{
    char fio[20];       // ФИО покупателя
    int id;             // порядковый номер
    char address[20];   // адрес покупателя
    char delivDate[20]; // дата доставки
    struct Customer *next;  //следующий покупатель
};
 
typedef struct Customer *PCust;   // массив структур
PCust Head = NULL;                // голова списка
 
char FILENAME[] = "C:\\Users\\Asus\\Desktop\\customers.txt";

PCust CreateCustomer(char *NewFio, char *NewAdress, char *NewData, int id);
PCust Find(PCust Head, char NewFio[]);
void AddFirst(PCust * Head, PCust NewCustomer);
void AddLast(PCust *Head, PCust NewCustomer);
void AddAfter(PCust p, PCust NewCustomer);
void DeleteNode(PCust *Head, PCust p);

int menu();
void writeToList(void);
void writeToFile(PCust customer);
void removeFromFile(PCust customer);
void addCustomer(void);
void deleteCustomer(void);
void sortCustomers(void);
 
void addNodeFromFile(char *fio, char *address, char *date, int id);
 
// TODO: Запись в список при старте программы
int main(){
    writeToList();
    while(1)
        switch (menu())
        {
            case 1:break;
            case 2:break;
            case 3:addCustomer();break;
            case 4:deleteCustomer();break;
            case 5:break;
            case 6:break;
            case 7:return 0;
            default:printf("Neverniy vibor\n");
        }
}
 
int menu()
{
    int choice = 0;
    do
    {
//        system("cls");//очищает содержание экрана
        printf("Menu\n");
        printf("1.New list\n");
        printf("2.Read from file\n");
        printf("3.Add customer\n");
        printf("4.Delete customer\n");
        printf("5.Change customer field\n");
        printf("6.Search in customers\n");
        printf("7.Exit\n");
        printf("Vash vibor? : ");
        scanf("%d", &choice);//ввод действия,которое хотим произвести
        system("cls");
    }while (choice>7);
    return choice;
}
 
void writeToList(void){
    FILE *f = fopen(FILENAME, "r");
 
//    if(f != NULL) {
//        fseek(f, 0, SEEK_END);
//        long size = ftell(f);
//
//        if (size != 0) {
            char line[512];
            int nullEtrCount = 0;
            PCust q = Head;
            int currentLine = 0;
 
            char fio[20];
            char id[20];
            char address[20];
            char delivDate[20];
 
            while (!feof(f)) {
                fgets(fio, 20, f);
                fgets(address, 20, f);
                fgets(delivDate, 20, f);
                fgets(id, 20, f);
 
                //Удаляет символ конца строки в фамилии
                int i = 0;
                while (fio[i] != '\n') {
                    i++;
                }
                fio[i] = '\0';
//        i = 0;
//        while(address[i] != '\n')
//        {
//            i++;
//        }
//        address[i] = '\0';
//        i = 0;
//        while(delivDate[i] != '\n')
//        {
//            i++;
//        }
//        delivDate[i] = '\0';
//        i = 0;
//        while(id[i] != '\n')
//        {
//            i++;
//        }
//        id[i] = '\0';
                addNodeFromFile(fio, address, delivDate, atoi(id));
            }
        }
//    }
//}
 
void addNodeFromFile(char *fio, char *address, char *date, int id) {
    PCust tempNode;
 
    tempNode = (PCust)malloc(sizeof(struct Customer));
    strcpy(tempNode->fio, fio);
    strcpy(tempNode->address, address);
    strcpy(tempNode->delivDate, date);
    tempNode->id = id;
 
    AddLast(&Head, tempNode);
}
 
void addCustomer(void) {
    struct Customer currentCustomer;
    printf("Vvedite FIO pokupatelya: ");
    scanf("%s",currentCustomer.fio);
    printf("Vvedite adres pokupatelya: ");
    scanf("%s",currentCustomer.address);
    printf("Vvedite datu dostavki: ");
    scanf("%s",currentCustomer.delivDate);
    printf("Vvedite nomer pokupatelya: ");
    scanf("%d",&currentCustomer.id);
    PCust customerNode = CreateCustomer(currentCustomer.fio, currentCustomer.address,
                                  currentCustomer.delivDate, currentCustomer.id);
    AddLast(&Head, customerNode);
    // Запись в файл
    writeToFile(customerNode);
}

void deleteCustomer(void) {
    char fio[20] = "\n";
 
    printf("-Spisok vseh familii-\n");
    // Выводим список всех фамилий в файле
    PCust q = Head;     // Начали с головы
    while(q != NULL)    // Пока не дошли до конца
    {
        if(q->fio != NULL){
            printf("%s\n",q->fio);
        }
        q = q->next;    // Переходим к следующему узлу
    }
 
    printf("Vvedite FIO pokupatelya dlya udaleniya: ");
    scanf("%s",fio);
 
    PCust target = Find(Head, fio);
    removeFromFile(target);
    DeleteNode(&Head, target);
    getch();
}
 
void sortCustomers(void) {
 
}
 
void writeToFile(PCust Head){
    FILE *f =fopen(FILENAME, "a");
    if( f != NULL)
    {
        PCust q = Head;     // Начали с головы
        while(q != NULL)    // Пока не дошли до конца
        {
            fprintf(f,"%s\n",q->fio);
            fprintf(f,"%s\n",q->address);
            fprintf(f,"%s\n",q->delivDate);
            fprintf(f,"%d\n",q->id);
            fprintf(f, "\n");
            q = q->next;    // Переходим к следующему узлу
        }
        fclose(f);
    }
}
 
void removeFromFile(PCust customer) {
 
    char str[512];
    char temp[512];
    int FioLineNumb = 1;
    int delLineNumb = 1;
    int currLineNumb = 0;
    int find_result = 0;
 
    FILE *f = fopen(FILENAME, "r");
 
    /**** Находим номер строки с искомым именем ****/
    while (fgets(temp, sizeof(temp), f) != NULL) {
        if ((strstr(temp, customer->fio)) != NULL) {
            delLineNumb = FioLineNumb;
            printf("Sovpadenie naideno na stroke: %d\n", FioLineNumb);
            //printf("\n%s\n", temp);
            find_result++;
        }
        FioLineNumb++;
    }
 
    if (find_result == 0) {
        printf("\nNichego ne naideno.\n");
    }
 
    /**** Создаем копию исходного файла ****/
    rewind(f);
    FILE *newF = fopen("C:\\Users\\Asus\\Desktop\\customers_new.txt", "w");
    while(fgets(str, sizeof(str), f) != NULL){
        currLineNumb++;
 
        // Если текущая строка не равняется строке, которую нужно удалить
        // удаляется 5 строк - одна структура
        if(currLineNumb != delLineNumb && currLineNumb != delLineNumb + 1 && currLineNumb != delLineNumb + 2 &&
                   currLineNumb != delLineNumb + 3 && currLineNumb != delLineNumb + 4) {
                       // Пишем массив в файл
                       fputs(str, newF);
                   }
    }
    fclose(f);
    fclose(newF);
    // Удаляем старый файл
    remove(FILENAME);
    // Переименовываем новый
    int result = rename("C:\\Users\\Asus\\Desktop\\customers_new.txt", FILENAME);
    if(result == 0){
        printf("Rename success.");
    } else{
        printf("Rename error.");        
    }
 
}

PCust CreateCustomer(char *NewFio, char *NewAdress, char *NewData, int id)
{
    PCust NewCustomer = malloc(sizeof *NewCustomer);
    strcpy(NewCustomer->fio, NewFio);
    strcpy(NewCustomer->address, NewAdress);
    strcpy(NewCustomer->delivDate, NewData);
    NewCustomer->id = id;
    NewCustomer->next = NULL;
    return NewCustomer;
}
 
// Добавление нового узла
void AddFirst(PCust * Head, PCust NewCustomer)
{
    NewCustomer->next = *Head;
    *Head = NewCustomer;
}
 
// Добавление узла между элементами
void AddAfter(PCust p, PCust NewCustomer)
{
    NewCustomer->next = p->next;
    p->next = NewCustomer;
}
 
// Добавить узел в конец списка
void AddLast(PCust *Head, PCust NewCustomer)
{
    PCust q = *Head;
 
    // Если список пустой
    if(*Head == NULL)
    {
//        printf("Add first");
        AddFirst(Head, NewCustomer);
        return;
    }
 
    // Цикл прохода до последнего узла списка
    while(q->next)
        q = q->next;
 
    // Добавление узла после заданного узла,
    // в данном случае после q
    AddAfter(q, NewCustomer);
}
 
// Поиск элемента в списке
PCust Find(PCust Head, char NewFio[]) // Ищем ФИО NewFio[], результат адрес узла
{
    PCust q = Head;
    // Пока не дошли до конца списка и слово не равно заданному
    while(q && strcmp(q->fio, NewFio))
        q = q->next;
    return q;
}
 
void DeleteNode(PCust *Head, PCust p)
{
    PCust q = *Head;
 
    if(*Head == p)          // Особый случай, если голова списка совпадает с удаляемым узлом
        Head = &(p->next);  // нужно поменять голову списка. Голова списка смещается на адрес следующего узла
    else{
        while(q && q->next != p)    // пока не находим предыдущий узел до удаляемого (p)
            q = q->next;            // делаем проход по списку
        if(q == NULL) return;
        q->next = p->next;          // если мы нашли узел перед удаляемым, переставляем ссылку ->next c узла перед удаляемым на следующий узел после удаляемого.
    }
    free(p); // освобождаем память под тот узел, который нам надо удалить.
}
Пример структуры файла customers.txt:
max
1
2
3

artem
4
5
6

nikita
7
8
9

Решение задачи: «Односвязный список и файл»

textual
Листинг программы
// Вывод списка
void DisplayList(PCust Head) {
    PCust current = Head;
 
    printf("--- Spisok vseh pokupatelei ---\n");
    while (current != NULL) {
        printf("%s\n", current->fio);
        current = current->next;
    }
    getch();
}

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

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

  1. DisplayList(PCust Head) - функция вывода списка покупателей на экран. В качестве параметра передается указатель на голову списка.
  2. PCust - структура данных, представляющая элемент списка.
  3. Head - указатель на голову списка.
  4. current - временная переменная, используемая для обхода списка.
  5. printf - функция вывода информации на экран.
  6. %s - спецификатор формата для вывода строки.
  7. getch - функция, предназначенная для приостановки выполнения программы до получения ввода от пользователя. Вывод списка покупателей осуществляется в цикле, пока не будет достигнут конец списка (т.е. значение current не станет равным NULL). Значение printf используется для вывода имени покупателя на экран. Значение getch используется для приостановки выполнения программы до получения ввода от пользователя.

Оцени полезность:

8   голосов , оценка 3.875 из 5
Похожие ответы