我正在尝试使用strtok()
从.txt文件初始化链接列表。
但是当我想要初始化名称(我的结构的第一个元素)时,strtok返回一个";(null)";。
然而,当我printf()
我的strElement
时,我得到了预期的名称。
治愈.c
#include "heals.h"
ListeHeals* initHeals()
{
ListeHeals* ListeHeals = malloc(sizeof(*ListeHeals));
char const* const fileName = "items/heals.txt";
FILE* file = fopen(fileName, "r");
if (file == NULL)
{
printf("Fichier non ouvert");
}
char line[256];
const char * separator = "|";
int count = 0;
while (fgets(line, sizeof(line), file)) {
char* strElement = strtok (line, separator);
while (strElement != NULL) {
Heals* heal = malloc(sizeof(*heal));
if(count == 0)
{
printf("%sn", strElement);
heal->name = strElement;
}
else if(count == 1)
{
heal->heal = atoi(strElement);
ListeHeals->first = heal;
}
strElement = strtok (NULL, separator);
count += 1;
}
count = 0;
}
fclose(file);
return ListeHeals;
}
void printListeHeals(ListeHeals* ListeHeals)
{
if (ListeHeals == NULL)
{
exit(EXIT_FAILURE);
}
Heals* actual = ListeHeals->first;
while (actual != NULL)
{
printf("Nom : %sn", actual->name);
printf("heal : %dnn", actual->heal);
actual = actual->next;
}
printf("NULLn");
}
输出第一行是我的printf。
这是文件heals.txt:
Potion de vie I|30
Potion de vie II|80
Potion de vie III|200
愈合结构(heal.h):
#ifndef heals_h
#define heals_h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Heals
{
char* name;
int heal;
struct Heals* next;
};
typedef struct Heals Heals;
struct ListeHeals
{
struct Heals* first;
};
typedef struct ListeHeals ListeHeals;
ListeHeals* initHeals();
void printListeHeals(ListeHeals* ListeHeals);
#endif
我期望的输出:
nom : Potion de vie I // I have (null)
heal : 30 // I already have it
欢迎任何帮助,谢谢!
有多个问题,您正在做比需要更复杂的事情。
最重要的是:
- 您不会将节点排入列表
- 您不会为每条新行重置
count
。这意味着您将不会处理任何其他行的名称 - 您不为字符串分配内存,而只将指针分配到
line
缓冲区。当函数完成时,这将超出范围 - 当您为每个令牌分配新节点时,您最终会得到一个持有名称的节点和一个持有另一个值的节点
更新:为什么这会导致你得到的结果?您为该名称分配了一个节点,但不将其放入列表中。然后分配另一个节点。此节点不包含名称,只包含第二个值。此节点将进入列表。当您打印列表的内容时,您将只找到第二个节点。
此外,您可以用任何新节点覆盖列表的标题,而不将它们链接在一起。
因此,无论你读了多少行,你的列表中都只会有一个节点。并且该节点将仅保存heal
值。
固定版本可能如下(未经测试):
ListeHeals* initHeals()
{
ListeHeals* ListeHeals = malloc(sizeof(*ListeHeals));
// TODO: Check for NULL
char const* const fileName = "items/heals.txt";
FILE* file = fopen(fileName, "r");
if (file == NULL)
{
printf("Fichier non ouvert");
// TODO: return with some error indication. You mustn't continue the function.
}
char line[256];
const char * separator = "|";
while (fgets(line, sizeof(line), file)) {
char* strElement = strtok (line, separator);
if (strElement != NULL) {
Heals* heal = malloc(sizeof(*heal));
// TODO: Check for NULL
// Handle the name
printf("%sn", strElement);
heal->name = malloc(strlen(strElement+1);
strcpy(heal->name, strElement);
// Handle the value
strElement = strtok (NULL, separator);
// TODO: Check for NULL
heal->heal = atoi(strElement);
// enqueue node into front position
heal->next = ListeHeals->first;
ListeHeals->first = heal;
}
else
printf("invalid file content: %sn", line);
}
fclose(file);
return ListeHeals;
}
这里有几个问题,其中大部分@Gerhardh已经在他们的答案中详细说明了。但真正导致你所问问题的是
- 在内部循环的每次迭代中创建一个新的
Heals
,只为每个循环中的一个成员设置一个值,并将每个成员指定为列表的头。因此,当您开始打印时,位于列表头的Heals
(当时是列表中唯一的一个)只有其heal
成员集,而没有其name
元素。一个不同的Heals
获得了相应的名称集,但该名称集随后被泄露
看起来Gerhard修改后的代码也修复了这一点。尽管他们没有列举这个特定的问题,但他们更自然的实现方法不容易犯这样的错误。
我试过了,结果是:
Potion de vie I
Potion de vie II
Potion de vie III
Nom : Potion de vie III
heal : 0
Nom : Potion de vie II
heal : 0
Nom : Potion de vie I
heal : 0