Я пытаюсь прочитать строки из текстового файла одну за другой и добавить их в связанный список. Узлы содержат саму строку и количество вхождений строки в файле. Таким образом, каждая строка будет добавлена в связанный список только один раз, если в связанном списке уже есть строка, переменная int num в узле должна быть увеличена на единицу. Использование массивов запрещено.
typedef struct node
{
char *word;
int num;
struct node *next;
} node;
node* addToHead(node *head, char word[])
{
if (Contains(head, word) == 0)
{
node *tmp = (node*)malloc(sizeof(node));
tmp->word = (char)malloc((sizeof(char)* strlen(word) + sizeof(char)));
tmp->word = word;
tmp->num = 1;
if (head == NULL)
{
tmp->next = NULL;
head = tmp;
}
}
return head;
}
node* add(node *head, char word[])
{
if (Contains(head, word) == 0)
{
node *iter = head;
node *tmp = (node*)malloc(sizeof(node));
tmp->word = (char)malloc((sizeof(char)* strlen(word) + sizeof(char)));
tmp->word = word;
tmp->num = 1;
if (head != NULL)
{
while(iter->next != NULL)
iter = iter->next;
iter->next = tmp;
tmp->next = NULL;
return head;
}
else if (head == NULL)
{
head = addToHead(head, word);
return head;
}
}
return head;
}
int Contains(node *head, char word[])
{
node *iter = head;
while(iter!= NULL)
{
if (strcmp(iter->word, word) == 0)
{
iter->num++;
return 1;
}
iter = iter->next;
}
return 0;
}
void printList(node *head)
{
node *iter = head;
while(iter!=NULL)
{
printf("word: %s || number of word: %d\n", iter->word, iter->num);
iter=iter->next;
}
}
int main()
{
node *head=NULL;
FILE* file= fopen("text.txt", "r");
char word[500];
for(int i = 0; fscanf(file, "%s", word) != EOF; i++)
{
head = add(head, word);
printList(head);
}
printList(head);
return 0;
}
В этом коде я переписываю связанный список каждый раз, когда добавляю новую строку в связанный список. Насколько я могу судить, он не создает новый узел, а перезаписывает слово на ранее добавленном слове. Запятые и точки не проблема. Я написал один и тот же абзац Lorem Ipsum 3 раза. Вывод такой:
Как мне создать отдельный узел для каждого слова при чтении слов из файла?
Возможно, вы слышали, что в C не следует приводить результат malloc
. С++ требует приведения типов, поэтому некоторые люди все равно это делают. Но чего вы определенно не хотите делать, так это приводить указатель к char
— вы попытаетесь сохранить 64-битное значение в 8 битах, что является рецептом катастрофы. Потеряйте состав и, пока вы это делаете, также замените sizeof(char)
на 1. У тебя все будет хорошо и хорошо, коротко malloc(strlen(word) + 1));
ОМГ большое спасибо. strcpy() решил все мои проблемы.
Использование массивов запрещено. Тогда у вас возникнут проблемы с хранением строк. Строки — это не что иное, как массив char
, где последний char
имеет значение '\0'
.
@AndrewHenle Выделение памяти и сохранение в ней данных (формирование строки) на самом деле не создает массив. OTOH, printf("word: %s || number of word: %d\n"
действительно использует массив. В любом случае "Использование массивов запрещено". является размышлением о слабом задании, подчеркивающем неправильные идеи.
Протестировано с массивом фиксированного размера из 6 строк, ваши коды работают правильно, и вывод соответствует ожидаемому:
int main()
{
node *head=NULL;
char * word[6] = {"Alpha", "Beta", "Alpha", "Teta", "Alpha", "Beta"};
for(int i = 0; i<6; i++)
{
head = add(head, word[i]);
}
printList(head);
return 0;
}
Выход :
word: Alpha || number of word: 3
word: Beta || number of word: 2
word: Teta || number of word: 1
Итак, как объясняется в комментариях, проблема скорее в том, как вы выделяете эти строки и/или читаете их из файла.
Как указано выше, есть несколько проблем с вашим кодом:
tmp->word = (char)malloc((sizeof(char)* strlen(word) + sizeof(char)));
. Malloc возвращает указатель на выделенный адрес, поэтому вы должны написать:
tmp->word = (char *)malloc((sizeof(char)* strlen(word) + sizeof(char)));
Вот почему вы не получаете ожидаемого результата: tmp->word = word;
, делая это, даже если вы выделяете новый узел, его поле слова указывает на текущее слово. Таким образом, когда вы читаете слово в цикле, вы случайно меняете head->word
. Так что вы должны делать это таким образом strcpy(tmp->word, word)
.
Я также добавил исправленный код для справки.
#include<stdio.h>
typedef struct node
{
char *word;
int num;
struct node *next;
} node;
node* addToHead(node *head, char word[])
{
if (Contains(head, word) == 0)
{
node *tmp = (node*)malloc(sizeof(node));
//tmp->word = (char)malloc((sizeof(char)* strlen(word) + sizeof(char)));
//tmp->word = word;
tmp->word = (char *)malloc((sizeof(char)* strlen(word) + sizeof(char)));
strcpy(tmp->word, word);
tmp->num = 1;
if (head == NULL)
{
tmp->next = NULL;
head = tmp;
}
}
return head;
}
node* add(node *head, char word[])
{
if (Contains(head, word) == 0)
{
node *iter = head;
node *tmp = (node*)malloc(sizeof(node));
//tmp->word = (char)malloc((sizeof(char)* strlen(word) + sizeof(char)));
//tmp->word = word;
tmp->word = (char *)malloc((sizeof(char)* strlen(word) + sizeof(char)));
strcpy(tmp->word, word);
tmp->num = 1;
if (head != NULL)
{
while(iter->next != NULL)
iter = iter->next;
iter->next = tmp;
tmp->next = NULL;
return head;
}
else if (head == NULL)
{
head = addToHead(head, word);
return head;
}
}
return head;
}
int Contains(node *head, char word[])
{
node *iter = head;
while(iter!= NULL)
{
if (strcmp(iter->word, word) == 0)
{
iter->num++;
return 1;
}
iter = iter->next;
}
return 0;
}
void printList(node *head)
{
node *iter = head;
while(iter!=NULL)
{
printf("word: %s || number of word: %d\n", iter->word, iter->num);
iter=iter->next;
}
}
int main()
{
node *head=NULL;
FILE* file= fopen("text.txt", "r");
char word[500];
for(int i = 0; fscanf(file, "%s", word) != EOF; i++)
{
head = add(head, word);
printList(head);
}
printList(head);
return 0;
}
Хм. Ваша проблема в том, где вы назначаете строку. Вы выделяете память с помощью
malloc
, а в следующей инструкции вы назначаете указатель, содержащий выделенную память, наword
. Это означает, что (а) у вас больше нет доступа к выделенной памяти, что является утечкой памяти, и (б) что все они имеют одни и те же данные, то есть все, что написано в линейном буфере, если он все еще «жив». C довольно явный, и после выделения достаточного количества байтов для длины строки и терминатора вы должныstrcpy
ввести строку в эту память.