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

时间:2022-11-19 13:31:20

I've looked at multiple other similar questions but none of them worked with my code, and I gave up trying to look things up.

我看过其他几个类似的问题但是没有一个能用我的代码,我放弃了尝试查看的东西。

I'm trying to create a program that takes each line, which has a book title, from a file into a character array, because I need to call each book later, so book[1], book[2], etc. However, I cannot figure out how to create the array with my struct.

我正在尝试创建一个程序,将每个具有书名的行从文件转换为字符数组,因为我需要稍后调用每本书,所以书[1],书[2]等等。 ,我无法弄清楚如何使用我的struct创建数组。

#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, it is greatly appreciated. Thank you!

如果有人有任何想法,我们非常感谢。谢谢!

1 个解决方案

#1


1  

The simplest approach is to declare an array of structs in main(), using the Num_Book macro that you defined in the preprocessor directives. Since you are using getline(), you do not even need to manually allocate memory to store the strings; instead you can let the getline() function do the work. Note that getline() is not a Standard C function, but it is POSIX, and also a GNU extension. On some systems you may need to enable this function using a feature test macro, which is included in the code below.

最简单的方法是使用您在预处理程序指令中定义的Num_Book宏在main()中声明一个结构数组。由于您使用的是getline(),因此您甚至不需要手动分配内存来存储字符串;相反,你可以让getline()函数完成工作。请注意,getline()不是标准C函数,但它是POSIX,也是GNU扩展。在某些系统上,您可能需要使用功能测试宏启用此功能,该宏包含在下面的代码中。

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

要利用getline()的自动内存分配功能,需要为第一个参数传入空指针,第二个参数应该是指向包含零值的size_t变量的指针。

In the loop that reads the data into the structs, temporary variables are used instead of directly assigning to the struct fields. This allows the current string to be inspected before the final assignment, so that empty lines are not stored as book records.

在将数据读入结构的循环中,使用临时变量而不是直接分配给结构字段。这允许在最终赋值之前检查当前字符串,以便空行不会存储为书记录。

Remember that getline() keeps the \n character, so if the current line is non-empty, the terminating newline is replaced with a \0 terminator. Then the values held by the temporary variables are assigned to the appropriate fields of the current BOOK struct, temp_name and temp_length are reset to NULL and 0, and i is incremented.

请记住,getline()保留\ n字符,因此如果当前行非空,则终止换行将替换为\ 0终止符。然后将临时变量保存的值分配给当前BOOK结构的相应字段,temp_name和temp_length重置为NULL和0,并且i递增。

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

你仍然需要释放getline()分配的内存,所以这应该在结束程序之前完成。

Note that in the original code, while you were checking to be certain that the file books.txt was successfully opened, you did not exit() here. This would lead to problems in the event that the file failed to open, when the program went on as if it had opened. You could handle the error differently; for example, it may be appropriate to instead ask the user for a different filename.

请注意,在原始代码中,当您检查确定文件books.txt已成功打开时,您没有在此处退出()。如果程序打开时文件无法打开,这将导致问题,就好像它已打开一样。你可以用不同的方式处理错误;例如,改为向用户询问不同的文件名可能是合适的。

#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 the books, you can modify the above code. Below is a version that does this, first by initializing a variable max_books to a reasonable starting value. Space is allocated and assigned to a pointer to BOOK, and when a new book is added the space is reallocated if necessary.

如果需要为书籍动态分配空间,可以修改上述代码。下面是一个执行此操作的版本,首先将变量max_books初始化为合理的起始值。空间被分配并分配给指向BOOK的指针,并且当添加新书时,如果需要,则重新分配空间。

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

添加完所有书籍后,可以将分配修剪为精确大小。请注意在sizeof参数中使用标识符而不是显式类型。如果类型在代码的未来迭代中发生变化,则这样不易出错且易于维护。另请注意,如果出现分配错误,realloc()将返回空指针。在这里,直接分配到书籍会导致先前存储的数据丢失,并导致内存泄漏。因此,新分配的地址首先存储在temp中;只有在不是NULL的情况下,temp的值才会分配给books。

The same allocations must be freed as before, but in addition, the 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;
}

#1


1  

The simplest approach is to declare an array of structs in main(), using the Num_Book macro that you defined in the preprocessor directives. Since you are using getline(), you do not even need to manually allocate memory to store the strings; instead you can let the getline() function do the work. Note that getline() is not a Standard C function, but it is POSIX, and also a GNU extension. On some systems you may need to enable this function using a feature test macro, which is included in the code below.

最简单的方法是使用您在预处理程序指令中定义的Num_Book宏在main()中声明一个结构数组。由于您使用的是getline(),因此您甚至不需要手动分配内存来存储字符串;相反,你可以让getline()函数完成工作。请注意,getline()不是标准C函数,但它是POSIX,也是GNU扩展。在某些系统上,您可能需要使用功能测试宏启用此功能,该宏包含在下面的代码中。

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

要利用getline()的自动内存分配功能,需要为第一个参数传入空指针,第二个参数应该是指向包含零值的size_t变量的指针。

In the loop that reads the data into the structs, temporary variables are used instead of directly assigning to the struct fields. This allows the current string to be inspected before the final assignment, so that empty lines are not stored as book records.

在将数据读入结构的循环中,使用临时变量而不是直接分配给结构字段。这允许在最终赋值之前检查当前字符串,以便空行不会存储为书记录。

Remember that getline() keeps the \n character, so if the current line is non-empty, the terminating newline is replaced with a \0 terminator. Then the values held by the temporary variables are assigned to the appropriate fields of the current BOOK struct, temp_name and temp_length are reset to NULL and 0, and i is incremented.

请记住,getline()保留\ n字符,因此如果当前行非空,则终止换行将替换为\ 0终止符。然后将临时变量保存的值分配给当前BOOK结构的相应字段,temp_name和temp_length重置为NULL和0,并且i递增。

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

你仍然需要释放getline()分配的内存,所以这应该在结束程序之前完成。

Note that in the original code, while you were checking to be certain that the file books.txt was successfully opened, you did not exit() here. This would lead to problems in the event that the file failed to open, when the program went on as if it had opened. You could handle the error differently; for example, it may be appropriate to instead ask the user for a different filename.

请注意,在原始代码中,当您检查确定文件books.txt已成功打开时,您没有在此处退出()。如果程序打开时文件无法打开,这将导致问题,就好像它已打开一样。你可以用不同的方式处理错误;例如,改为向用户询问不同的文件名可能是合适的。

#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 the books, you can modify the above code. Below is a version that does this, first by initializing a variable max_books to a reasonable starting value. Space is allocated and assigned to a pointer to BOOK, and when a new book is added the space is reallocated if necessary.

如果需要为书籍动态分配空间,可以修改上述代码。下面是一个执行此操作的版本,首先将变量max_books初始化为合理的起始值。空间被分配并分配给指向BOOK的指针,并且当添加新书时,如果需要,则重新分配空间。

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

添加完所有书籍后,可以将分配修剪为精确大小。请注意在sizeof参数中使用标识符而不是显式类型。如果类型在代码的未来迭代中发生变化,则这样不易出错且易于维护。另请注意,如果出现分配错误,realloc()将返回空指针。在这里,直接分配到书籍会导致先前存储的数据丢失,并导致内存泄漏。因此,新分配的地址首先存储在temp中;只有在不是NULL的情况下,temp的值才会分配给books。

The same allocations must be freed as before, but in addition, the 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;
}