2017-02-17 6 views
5

Я просмотрел несколько других подобных вопросов, но ни один из них не работал с моим кодом, и я отказался от попыток взглянуть на вещи.Создание массива из текстового файла для вызова каждой строки по отдельности

Я пытаюсь создать программу, которая берет каждую строку, которая имеет название книги, из файла в массив символов, потому что мне нужно позвонить каждую книгу позже, так что книга [1], книга [2] и т. д. Однако я не могу понять, как создать массив с моей структурой.

#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); 
} 

Если у кого-то есть какие-либо идеи, то мы очень благодарны. Спасибо!

+0

Читали http://stackoverflow.com/questions/10468128/how-do-you-make-an-array-of-structs-in-c –

+0

Почему является 'SSIZE_T название, элемент' настоящее время? Вы не используете его или не инициализируете его, что вызывает беспокойство. Вы хоть представляете, сколько книг вам нужно обработать? Можете ли вы предварительно выделить массив структур 'BOOK', или вам нужно сделать это вручную (динамически) с помощью' malloc() 'et al? –

+0

'Book = (BOOK *) malloc (sizeof (BOOK));' не создаст пространства для поля book_name структуры BOOK. Сначала вам нужно сначала прочитать строку из файла в буфер, а затем выделить место для имени book_name. – bruceg

ответ

0

Самый простой способ - объявить массив структур в main(), используя макрос Num_Book, который вы определили в директивах препроцессора. Поскольку вы используете getline(), вам даже не нужно вручную выделять память для хранения строк; вместо этого вы можете позволить функции getline() выполнить эту работу. Обратите внимание, что getline() не является стандартной функцией C, но это POSIX, а также расширение GNU. В некоторых системах вам может потребоваться включить эту функцию с помощью макроса проверки функций, который включен в приведенный ниже код.

Чтобы воспользоваться возможностью автоматического выделения памяти getline(), вам необходимо передать нулевой указатель для первого аргумента, а второй аргумент должен быть указателем на переменную size_t, содержащую значение нуля.

В цикле, который считывает данные в структуры, используются временные переменные вместо прямого назначения поля struct. Это позволяет проверить текущую строку перед окончательным назначением, чтобы пустые строки не сохранялись в виде записей в книге.

Помните, что getline() хранит символ \n, поэтому, если текущая строка не пуста, завершающая новая строка заменяется терминатором \0. Затем значения, удерживаемые временными переменными, назначаются соответствующим полям тока BOOK struct, temp_name и temp_length сбрасываются на NULL и 0, а i увеличивается.

Вам по-прежнему необходимо освободить память, выделенную getline(), поэтому это должно быть сделано до окончания программы.

Обратите внимание, что в исходном коде, пока вы проверяли, что файл books.txt был успешно открыт, вы здесь не exit(). Это приведет к проблемам в случае, если файл не открывается, когда программа продолжалась, как если бы она открылась. Вы можете обрабатывать ошибку по-разному; например, может потребоваться вместо этого попросить пользователя указать другое имя файла.

#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; 
} 

выход программы:

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

Если вам необходимо динамически выделять место для книг, вы можете изменить код выше. Ниже приведена версия, которая делает это, сначала путем инициализации переменной max_books до разумного начального значения. Пространство выделяется и присваивается указателю на BOOK, а при добавлении новой книги пространство перераспределяется, если необходимо.

После того, как все книги были добавлены, распределение можно обрезать до нужного размера. Обратите внимание на использование идентификаторов вместо явных типов в аргументах sizeof.Это менее подвержено ошибкам и легче поддерживать, если типы изменяются в будущих итерациях кода. Также обратите внимание, что realloc() вернет нулевой указатель в случае ошибки выделения. Здесь прямое присвоение books приведет к потере ранее сохраненных данных и утечке памяти. По этой причине адрес нового распределения сначала сохраняется в temp; значение temp присваивается books, если оно не NULL.

Те же распределения должны быть освобождены по-прежнему, но, кроме того, также должен быть освобожден динамически выделенный массив.

#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

Я закрыл книгу в конце, но забыл добавить это из моей копии и вставленного кода, но это было действительно полезно! – Wheeeeeecode

+0

Если я не уверен в конкретном количестве книг, и мне нужно использовать getline, чтобы выяснить, сколько у меня книг, как я могу использовать это, чтобы я все еще мог называть '(printf ("% s \ n", книги [i] .book_name); 'вне цикла while – Wheeeeeecode

+0

@ Wheeeeeecode. Я добавил версию, которая использует динамическое распределение. Она отслеживает количество сохраненных книг и выводит результаты за пределы цикла чтения. –