最简单的方法是在
main()
中声明一个结构体数组,使用您在预处理器指令中定义的
Num_Book
宏。由于您使用了
getline()
,因此甚至不需要手动分配内存来存储字符串;相反,您可以让
getline()
函数完成这项工作。请注意,
getline()
不是标准的 C 函数,但它是 POSIX 和 GNU 扩展。在某些系统上,您可能需要使用特性测试宏来启用此函数,该宏包含在下面的代码中。
为了利用
getline()
的自动内存分配功能,您需要将第一个参数传递为 null 指针,第二个参数应该是一个指向包含值为零的
size_t
变量的指针。
在读取数据到结构体的循环中,使用临时变量而不是直接分配给结构体字段。这样可以在最终分配之前检查当前字符串,以便空行不会被存储为书籍记录。
记住
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') {
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;
}
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;
if ((find = strchr(temp_name, '\n')) != NULL) {
*find = '\0';
}
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;
}
books[i].book_name = temp_name;
books[i].length = temp_length;
books[i].title = temp_title;
temp_name = NULL;
temp_length = 0;
++i;
}
if ((temp = realloc(books, (sizeof *books) * num_books)) == NULL) {
fprintf(stderr, "Unable to trim books allocation\n");
exit(EXIT_FAILURE);
}
books = temp;
printf("Book list\n");
for (i = 0; i < num_books; i++) {
printf("%s\n", books[i].book_name);
}
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;
}
ssize_t title;
元素?你既不使用它也不初始化它,这很令人担忧。你有没有想过要处理多少本书?你能预先分配一个BOOK
结构体数组吗?还是需要手动(动态)使用malloc()
等函数来完成? - Jonathan LefflerBook = (BOOK *) malloc (sizeof(BOOK));
不会为 BOOK 结构体中的 book_name 字段创建任何空间。您需要先从文件中读取字符串到缓冲区,然后再为 book_name 分配空间。 - bruceg