从文本文件创建数组以单独调用每一行

5

我看过多个类似的问题,但是它们都不能适用于我的代码,我放弃了查找。

我正在尝试创建一个程序,将文件中每一行(包含书名)存入字符数组中,因为我需要稍后调用每本书,如book[1]、book[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);
}

如果有任何想法,非常感激。谢谢!


你读过https://dev59.com/OGkv5IYBdhLWcg3wnCLH吗? - Fredrik Pihl
为什么存在 ssize_t title; 元素?你既不使用它也不初始化它,这很令人担忧。你有没有想过要处理多少本书?你能预先分配一个 BOOK 结构体数组吗?还是需要手动(动态)使用 malloc() 等函数来完成? - Jonathan Leffler
Book = (BOOK *) malloc (sizeof(BOOK)); 不会为 BOOK 结构体中的 book_name 字段创建任何空间。您需要先从文件中读取字符串到缓冲区,然后再为 book_name 分配空间。 - bruceg
1个回答

1
最简单的方法是在 main() 中声明一个结构体数组,使用您在预处理器指令中定义的 Num_Book 宏。由于您使用了 getline(),因此甚至不需要手动分配内存来存储字符串;相反,您可以让 getline() 函数完成这项工作。请注意,getline() 不是标准的 C 函数,但它是 POSIX 和 GNU 扩展。在某些系统上,您可能需要使用特性测试宏来启用此函数,该宏包含在下面的代码中。
为了利用 getline() 的自动内存分配功能,您需要将第一个参数传递为 null 指针,第二个参数应该是一个指向包含值为零的 size_t 变量的指针。
在读取数据到结构体的循环中,使用临时变量而不是直接分配给结构体字段。这样可以在最终分配之前检查当前字符串,以便空行不会被存储为书籍记录。
记住getline()会保留\n字符,所以如果当前行非空,则终止的换行符将被替换为\0终止符。然后,临时变量持有的值将分配给当前BOOK struct的适当字段,temp_nametemp_length重置为NULL0,并且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不是NULL时,才将temp的值分配给books
与以前一样,必须释放相同的分配,但是除此之外,还必须释放动态分配的数组。
#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;
}

我在书末关闭了书,但忘记将此添加到我复制粘贴的代码中,不过这真的很有帮助! - Wheeeeeecode
如果我不确定书的具体数量,需要使用getline来确定我有多少本书,那么如何使用它,以便我仍然可以在while循环之外调用'(printf("%s\n", books[i].book_name);'? - Wheeeeeecode
@Wheeeeeecode-- 我添加了一个使用动态分配的版本。它跟踪存储的书籍数量,并在读取循环之外打印结果。 - ad absurdum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接