Creating an array from a text file to individually call each line

I looked at several other similar issues, but none of them worked with my code, and I gave up trying to take a look at things.

I'm trying to create a program that takes every line that has a book name from a file to an array of characters, because I need to call each book later, so book [1], book [2], etc. However, I cannot figure out how to create an array with my structure.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> #define Num_Book 9 typedef struct book { char *book_name; size_t length; ssize_t title; }BOOK; int main(int argc, char* argv[]) { BOOK *Book; Book = (BOOK *) malloc (sizeof(BOOK)); (*Book).book_name = NULL; (*Book).length = 0; char title_arr[Num_Book][50]; //this is the array that I tried creating, //but it kept giving me warnings when I tried to compile //opening my file FILE *f_books; f_books = fopen("books.txt", "r"); if (f_books == NULL) { printf("Cannot open file for reading.\n"); } printf("Book list\n"); while (((*Book).title = getline(&(*Book).book_name, &(*Book).length, f_books)) != -1) { printf("%s", (*Book).book_name); } 

If anyone has any ideas, this is much appreciated. Thanks!

+5
source share
1 answer

The easiest way is to declare an array of structures in main() using the Num_Book macro that you defined in the preprocessor directives. Since you are using getline() , you don’t even need to manually allocate memory to hold strings; instead, you can let getline() function do the job. Note that getline() not a standard C function, but it is POSIX, as well as a GNU extension. On some systems, you may need to enable this function using the function check macro, which is included in the code below.

To take advantage of the automatic allocation of getline() memory, you need to pass a null pointer for the first argument, and the second argument must be a pointer to the size_t variable containing the null value.

In a loop that reads data in structs, temporary variables are used instead of directly assigning the struct field. This allows you to check the current line before final assignment, so that empty lines are not saved as entries in the book.

Remember that getline() stores the \n character, so if the current line is not empty, the terminating new line is replaced by the terminator \0 . Then the values ​​held by temporary variables are assigned to the corresponding fields of the current BOOK struct , temp_name and temp_length reset to NULL and 0 , and i increases.

You still need to free the memory allocated by getline() , so this should be done before the program ends.

Note that in the source code, while you checked that the books.txt file was successfully opened, you are not exit() . This will lead to problems if the file does not open when the program continues, as if it opened. You can handle the error in different ways; for example, it may be appropriate to instead request a different file name from the user.

 #define _POSIX_C_SOURCE 200809L #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> #define Num_Book 9 typedef struct book { char *book_name; size_t length; ssize_t title; } BOOK; int main(void) { BOOK books[Num_Book]; FILE *f_books; f_books = fopen("books.txt", "r"); if (f_books == NULL) { fprintf(stderr, "Cannot open file for reading.\n"); exit(EXIT_FAILURE); } printf("Book list\n"); char *temp_name = NULL; size_t temp_length = 0; ssize_t temp_title; char *find; size_t i = 0; while ((temp_title = getline(&temp_name, &temp_length, f_books)) != -1 && temp_name[0] != '\n') { /* Replace newline with '\0' */ if ((find = strchr(temp_name, '\n')) != NULL) { *find = '\0'; } books[i].book_name = temp_name; books[i].length = temp_length; books[i].title = temp_title; temp_name = NULL; temp_length = 0; printf("%s\n", books[i].book_name); ++i; } /* Still need to free allocated memory */ for (size_t j = 0; j < i; j++) { free(books[j].book_name); } if (temp_name) { free(temp_name); } if (fclose(f_books) != 0) { fprintf(stderr, "Unable to close file\n"); exit(EXIT_FAILURE); } return 0; } 

Program output:

 Book list The Sound and the Fury So Long, and Thanks for All the Fish Gargantua and Pantagruel Foundation 

If you need to dynamically allocate space for books, you can change the code above. The following is the version that does this, first by initializing the variable max_books to a reasonable initial value. The space is allocated and assigned to the BOOK pointer, and when you add a new book, the space is redistributed if necessary.

After all books have been added, the distribution can be trimmed to the exact size. Note the use of identifiers instead of explicit types in sizeof arguments. This is less error prone and easier to maintain if types change in future code iterations. Also note that realloc() will return a null pointer in case of a distribution error. Here, direct assignment of books will lead to the loss of previously saved data and memory leak. For this reason, the address of the new distribution is first stored in temp ; temp assigned to books only if it is not NULL .

The same distributions must be freed as before, but in addition, a dynamically allocated array must also be freed.

 #define _POSIX_C_SOURCE 200809L #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> typedef struct book { char *book_name; size_t length; ssize_t title; } BOOK; int main(void) { FILE *f_books; f_books = fopen("books.txt", "r"); if (f_books == NULL) { fprintf(stderr, "Cannot open file for reading.\n"); exit(EXIT_FAILURE); } char *temp_name = NULL; size_t temp_length = 0; ssize_t temp_title; char *find; size_t i = 0; BOOK *books; BOOK *temp; size_t max_books = 10; size_t num_books = 0; if ((books = malloc((sizeof *books) * max_books)) == NULL) { fprintf(stderr, "Unable to allocate books\n"); exit(EXIT_FAILURE); } while ((temp_title = getline(&temp_name, &temp_length, f_books)) != -1 && temp_name[0] != '\n') { ++num_books; /* Replace newline with '\0' */ if ((find = strchr(temp_name, '\n')) != NULL) { *find = '\0'; } /* Reallocate books if more space is needed */ if (num_books == max_books) { max_books *= 2; if ((temp = realloc(books, (sizeof *books) * max_books)) == NULL) { fprintf(stderr, "Unable to reallocate books\n"); exit(EXIT_FAILURE); } books = temp; } /* Store book data */ books[i].book_name = temp_name; books[i].length = temp_length; books[i].title = temp_title; temp_name = NULL; temp_length = 0; ++i; } /* If you need books to be trimmed to minimum size */ if ((temp = realloc(books, (sizeof *books) * num_books)) == NULL) { fprintf(stderr, "Unable to trim books allocation\n"); exit(EXIT_FAILURE); } books = temp; /* Display list of books */ printf("Book list\n"); for (i = 0; i < num_books; i++) { printf("%s\n", books[i].book_name); } /* Still need to free allocated memory */ for (i = 0; i < num_books; i++) { free(books[i].book_name); } free(books); if (temp_name) { free(temp_name); } if (fclose(f_books) != 0) { fprintf(stderr, "Unable to close file\n"); exit(EXIT_FAILURE); } return 0; } 
0
source

Source: https://habr.com/ru/post/1264365/


All Articles