Как удалить структуру из массива структур и правильно переписать файл, используя fwrite () - PullRequest
0 голосов
/ 09 мая 2018

Привет всем, я новичок в C. Мне пришлось создать «библиотеку», где я могу хранить некоторые книги и вносить в них некоторые изменения (добавить книгу, удалить книгу или изменить ее), используя массив структур, то я должен сохранить его в файл.

Проблема в том, что должна быть возможность удалить книгу и перезаписать изменения в файле, открытом в режиме "r + b", с помощью функции fwrite ().

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

Ex.

  • Книга1
  • Book2
  • book3

После удаления «Book2» и прокрутки вниз структур:

  • Книга1
  • book3
  • book3

После назначения пустой структуры последней прочитанной:

  • Книга1
  • book3
  • Random_Stuff

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

  • Книга1
  • book3
  • book3

Если я перезаписываю файл тремя структурами, я получаю:

  • Книга1
  • book3
  • Random_Stuff

И это разумно, но как я могу перезаписать весь файл только в этом случае первыми двумя структурами, использующими fwrite ()?

Это моя структура

typedef struct book {
        char title[MAXTIT];
        char autor[MAXAUT];
        float price;
} info;

Это функция удаления книги:

void remove_book(info bibl[], int * num) {

// Empty structure to assign to the last structure after the removal
info empty;

char title[256];

puts("Enter the title of the book you want to remove:");
read(title, 256);

// Scrolling down the structures to cover the removed one
for (int i = 0 ; i < *num ; i++)
    if (strcmp(title, bibl[i].title) == 0) {
        for (int index = i ; index < *num ; index++)
            bibl[index] = bibl[index + 1];
        bibl[*num] = empty;
        (*num)--;
        break;
    }

for (int index = 0 ; index < *num ; index++)
    printf("%s, autor: %s, price: %.2f €\n", bibl[index].title, bibl[index].autor, bibl[index].price);
}

Это полный код (на всякий случай):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define MAXTIT  40          // Maximum lenght of the title
#define MAXAUT  40          // Maximum lenght of the name of the autor
#define MAXBOOK 10          // Maximum number of books

typedef struct book {
        char title[MAXTIT];
        char autor[MAXAUT];
        float price;
} info;

int size = sizeof(info);
int counter = 0;
int counterp;
FILE * fp;
char file_name[64];

int menu(void);
void add_book(info [], int *);
void remove_book(info [], int *);
void modify_book(info [], int);
void input(info [], int *, int *);
char *read(char *, int);

int main(void) {

info bibl[MAXBOOK];
int index, choice;

puts("Please, enter the file to be read:");
read(file_name, 64);

// If returns a NULL pointer puts an error message and ends the program
if (!(fp = fopen(file_name, "r+b"))) {
    fprintf(stderr, "An error occurred while trying to open \"%s\".", file_name);
    exit(1);
}

// Read the content of the file
input(bibl, &counter, &counterp);

    // If the file is empty lets the user add a book
    if (counterp == 0) {
        add_book(bibl, &counter);
        fwrite(&bibl[counterp], size, counter - counterp, fp);

        // If the user added a book shows the content of the file
        if (counter > 0) {
            puts("Updated file content:");
            for (index = 0 ; index < counter ; index++)
                printf("%d) %s, autor: %s, price: %.2f €\n", index + 1, bibl[index].title, bibl[index].autor, bibl[index].price);
            putchar('\n');
        }
    }

    // If there is something in the file lets the user choose between some options (add, remove, modify a book or simply quit)
    if (counter > 0) {

        // Menu returns a value for the switch function
        choice = menu();

        // Repeats the choice until the "Quit" option has been chosen
        while (choice) {

            switch(choice) {

            // First case, reads from the file, adds the book and writes everything in the file
            case 1 :    input(bibl, &counter, &counterp);
                    add_book(bibl, &counter);
                    fwrite(&bibl[counterp], size, counter - counterp, fp);
                    break;

            // Second case, reads from the file, (should) removes the book and overwrites everything in the file
           case 2 : input(bibl, &counter, &counterp);
                    remove_book(bibl, &counter);
                    rewind(fp);                 // Rewind to overwrite the new values in the file
                    fwrite(&bibl[0], size, counterp, fp);
                    break;

            // Third case, reads from the file, modifies the book and overwrites everything in the file
           case 3 : input(bibl, &counter, &counterp);
                    modify_book(bibl, counter);
                    rewind(fp);                 // Same as before
                    fwrite(&bibl[0], size, counter, fp);
                    break;

            // Forth case, quits the menu
           case 4 : break;

            // Should never activate but I put it in case I'd like to modify something in the menu ecc...
          default : fputs("You entered wrong data!", stderr);
                    break;

          }

        // If a change has been made shows the new content
        if (choice >= 1 && choice <= 3) {
            puts("Updated file content:");

            for (index = 0 ; index < counter ; index++)
                printf("%d) %s, autor: %s, price: %.2f €\n", index + 1, bibl[index].title, bibl[index].autor, bibl[index].price);

            putchar('\n');
        }

        // Exit the loop in case of "Quit" option
        if (choice == 4)
            break;

        choice = menu();
    }

} else
    puts("No books? What a pity!");

// If fclose() != NULL, an error occurred
if (fclose(fp))
    fprintf(stderr, "An error occurred while trying to close \"%s\".", file_name);

puts("Ending process...");

return 0;
}

// Function that shows the options of the menu
int menu(void) {

int num;

puts("Choose between:\n1) Add a book\n2) Remove book\n3) Modify a book\n4) Quit");

while (scanf("%d", &num) != 1 || num < 1 || num > 4)
    puts("Choice between:\n1) Add a book\n2) Remove book\n3) Modify a book\n4) Quit");

while (getchar() != '\n');

return num;
}

// Function used to add a book to the file (works fine)
void add_book(info bibl[], int * num) {

int index;
bool flag = false;

puts("Please, enter a new title ([enter] at the start of the line ends the process):");

while (*num < MAXBOOK && read(bibl[*num].title, MAXTIT) && bibl[*num].title[0] != '\0') {

    puts("Now enter the autor.");
    read(bibl[*num].autor, MAXAUT);

    for (index = 0 ; index < *num ; index++)
        if (strcmp(bibl[index].title, bibl[*num].title) == 0 && strcmp(bibl[index].autor, bibl[*num].autor) == 0) {
            puts("This book is already in your library.");
            puts("Please, enter a new title ([enter] at the start of the line ends the process):");
            flag = true;
            break;
        }

    if (flag)
        continue;

    puts("And finally enter the price.");
    scanf("%f", &bibl[(*num)++].price);

    while (getchar() != '\n');

    if (*num < MAXBOOK)
        puts("\nPlease, enter a new title ([enter] at the start of the line ends the process):");
    }
}

// Function that should work and do what it's meant to do but it doesn't for I don't know what reason
void remove_book(info bibl[], int * num) {

// Empty structure to assign to the last structure after the removal
info empty;

char title[256];

puts("Enter the title of the book you want to remove:");
read(title, 256);

// Scrolling down the structures to cover the removed one
for (int i = 0 ; i < *num ; i++)
    if (strcmp(title, bibl[i].title) == 0) {
        for (int index = i ; index < *num ; index++)
            bibl[index] = bibl[index + 1];
        bibl[*num] = empty;
        (*num)--;
        break;
    }

for (int index = 0 ; index < *num ; index++)
    printf("%s, autor: %s, price: %.2f €\n", bibl[index].title, bibl[index].autor, bibl[index].price);
}

// Function used to change a book's informations
void modify_book(info bibl[], int num) {

    char title[256];
    int choice;

    puts("Enter the title of the book you want to modify:");
    read(title, 256);

    puts("Choose what you want to modify:\n1) Title\n2) Autor\n3) Price\n4) Everything");
    while(scanf("%d", &choice) != 1 || choice < 1 || choice > 4)
        puts("Choose what you want to modify:\n1) Title\n2) Autor\n3) Price\n4) Everything");

    while (getchar() != '\n');

    for (int i = 0 ; i < num ; i++) {
        if (strcmp(title, bibl[i].title) == 0) {

            switch (choice) {


            case 1 :    puts("Please, enter the new title of the book:");
                        read(bibl[i].title, MAXTIT);
                        break;

            case 2 :    puts("Enter the new autor of the book:");
                        read(bibl[i].autor, MAXAUT);
                        break;

            case 3 :    puts("Enter the new price:");
                        scanf("%f", &bibl[i].price);
                        while (getchar() != '\n');
                        break;

            case 4 :    puts("Please, enter the title of the book:");
                        read(bibl[i].title, MAXTIT);
                        puts("Now enter the autor of the book:");
                        read(bibl[i].autor, MAXAUT);
                        puts("And finally its cost:");
                        scanf("%f", &bibl[i].price);
                        while (getchar() != '\n');
                        break;

            default :   break;

            }

            break;
        }
    }

    for (int index = 0 ; index < num ; index++)
        printf("%s, autor: %s, price: %.2f €\n", bibl[index].title, bibl[index].autor, bibl[index].price);
}

// Function that reads from the file in which we are saving the structures
void input(info bibl[], int * counter, int * counterp) {

    *counter = 0;
    rewind(fp);

    while (*counter < MAXBOOK && fread(&bibl[*counter], size, 1, fp) == 1) {

        if (*counter == 0)
            printf("Actual content of the file \"%s\":\n", file_name);

        printf("%d) %s by %s: %.2f €\n", *counter + 1, bibl[*counter].title, bibl[*counter].autor, bibl[*counter].price);
        (*counter)++;
    }

    if (*counter > 0 && *counter < MAXBOOK)
        putchar('\n');

    *counterp = *counter;
    if (*counter == MAXBOOK) {
        fprintf(stderr, "The file \"%s\" is full.", file_name);
        exit(1);
    }
}

// Better alternative for gets()
char *read(char * str, int len) {

    char * res, * here;

    if (res = fgets(str, len, stdin))
        if (here = strchr(str, '\n'))
            *here = '\0';
        else
            while (getchar() != '\n');

    return res;
}
...