C语言 我的链表正在打印文本文件中所有元素的最后一个单词



此代码正在读取文本文件并将每个单词插入链表中。 我是链表的新手,已经为此工作了四个小时,我一生都无法弄清楚这一点。

这到底是怎么回事呢?我已经检查了我知道如何做的所有方法,并且我一生都无法正确打印链表。我相信这与push/append功能有关。不知何故,它覆盖了链表中之前的所有内容。也许printlist函数正在覆盖所有内容,但我看不出它是如何做到这一点的。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
// A complete working C program to demonstrate all insertion methods
// on Linked List  
// A linked list node
struct Node;
void push(struct Node **head_ref, char *new_data);
void insertAfter(struct Node *prev_node, char *new_data);
void append(struct Node **head_ref, char *new_data);
void printList(struct Node *node);
int LINECOUNT(FILE *(*stream), char *filename);
struct Node {
char *data;
struct Node *next;
};
/* Given a reference (pointer to pointer) to the head of a list and 
an int, inserts a new node on the front of the list. */
void push(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
/* 2. put in the data  */
new_node->data  = new_data;
printf("push data:%s ", new_data);
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
}
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end  */
void append(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
struct Node *last = *head_ref;  /* used in step 5*/
/* 2. put in the data  */
new_node->data  = new_data;
printf("push data:%s ", new_data);
/* 3. This new node is going to be the last node, so make next of
it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL) {
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
// This function prints contents of linked list starting from head
void printList(struct Node *node) {
while (node != NULL) {
printf(" %s ", node->data);
node = node->next;
}
}
int LINECOUNT(FILE *(*stream), char *filename) {            
int size = 0;
size_t chrCount;
char *text;
if ((*stream = fopen(filename, "r")) == NULL) {
printf("LC Could not open hw8 data file.n");
exit(0);
}
while (1) {
text = NULL;
getline(&text, &chrCount, *stream); 
free(text); /*free text*/
if (feof(*stream))
break;
size++;
}
rewind(*stream);
return size;
}
/*int wordCount(FILE *(*stream), char *filename, int lineCount) {
char ch;
int wordcount = 0;
int charcount = 0; 
*stream = fopen(filename, "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (*stream) {
while ((ch = getc(*stream)) != EOF) {
if (ch != ' ' && ch != 'n') {
charcount++;
}
if (ch == ' ' || ch == 'n') { 
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == 'n') {
x++;
}
}
if (charcount > 0) {
wordcount++;
charcount++;
}
} else {
printf("Failed to open the filen");
}
// rewind(*stream);
return lineArr;
}*/
int main(void) {
char *fn = "hw8data.txt";
int lineCount;
FILE *stream;
lineCount = LINECOUNT(&stream, fn);
//int lineArr[lineCount];
//int lineArr[];//lineArr[0] = 4 would say the first line has 4 words. using this data for strtok
//lineArr = wordCount(&stream, fn, lineCount);
//-------------------------------------
char ch;
int wordcount = 0;
int charcount = 0; 
stream = fopen("./hw8data.txt", "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (stream) {
while ((ch = getc(stream)) != EOF) {
if (ch != ' ' && ch != 'n') {
charcount++;
}
if (ch == ' ' || ch == 'n') {
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == 'n') {
x++;
}
}
//if (charcount > 0) { wordcount++; charcount++; }
} else {
printf("Failed to open the filen");
}
/* Start with the empty list */
struct Node *head = NULL;
rewind(stream);
char *sArr = malloc(42 * sizeof(char));
fscanf(stream, "%s ", sArr);
printf("%s ", sArr);
push(&head, sArr);
fscanf(stream, "%s ", sArr);
printf("%s ",sArr);
append(&head, sArr);
printList(head);
return 0;
}
char* sArr=malloc(42*sizeof(char));
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
push(&head,sArr);
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
append(&head,sArr);

您将相同的值添加到列表中两次,这是您从唯一一次调用malloc中返回的值。如果希望两个节点保存不同的值,请不要将相同的值添加两次。一个丑陋的修复是,如果push(&head,sArr)后您添加了另一个sArr = malloc(42*sizeof(char));。这样,您对append的调用将向列表添加不同的值。

如果未看到此信息,请添加代码以在打印列表时输出node->data的值。您将看到两个节点都有指向同一内存块的指针,即您从该调用中返回的值malloc

但是,如果您的列表条目拥有其内容,那会更加优雅。这将需要像pushappend这样的函数来分配自己的指针,将字符串复制到其中,并使用这些新指针。用于销毁列表的代码可能会对指向的数据以及节点调用free

我会建议一种完全不同的方法。

我会使用 C99 灵活的数组成员来存储每个单词。 另外,由于我不希望我的代码可作为家庭作业答案提交,因此我将展示如何使用宽字符输入来执行此操作。 (基本上在所有操作系统上,除了Windows之外,它将非ASCII字符(如Ö和Ø(视为字母,如果您的区域设置说它们是字母。

struct word {
struct word  *next;
wchar_t       data[];  /* Flexible array member */
};

我会使用一个辅助函数,从广泛的流中读取下一个单词,跳过任何非单词字符(我假设是字母数字字符,即字母和数字(:

struct word *wide_word(FILE *input)
{
struct word *w = NULL, *tempw;
size_t       max = 0;  /* No characters allocated in w yet */
size_t       len = 0;  /* No characters in w yet */
wint_t       c;
/* NULL input is not allowed. */
if (!input) {
errno = EINVAL;
return NULL;
}
/* Also fail if the stream is already in an error state. */
if (ferror(input)) {
errno = EIO;
return NULL;
}
c = getwc(input);
/* Skip leading non-word characters. */
while (c != WEOF && !iswalnum(c))
c = getwc(input);
/* End of input? */
if (c == WEOF) {
errno = 0;
return NULL;
}
/* Append each wide word character. */
while (c != WEOF && iswalnum(c)) {
/* Need to reallocate? */
if (len >= max) {
/* Since words are usually short, let's allocate
in chunks of 64 wide characters. */
max = (len | 63) + 65;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
/* Out of memory. */
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Append. */
w->data[len++] = c;
c = getwc(input);
}
/* Although not useful for this particular case,
we normally want to keep the separator intact. */
if (c != WEOF)
ungetwc(c, input);
/* Optimize the memory allocated to the word. */
if (max != len + 1) {
max = len + 1;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Terminate the wide string in w. */
w->data[len] = L'';
/* Success! */
return w;
}

我个人更喜欢将新节点附加到列表中,然后反转整个列表:

struct word *reverse_list(struct word *oldlist)
{
struct word *newlist = NULL;
struct word *w;
while (oldlist) {
w = oldlist;
oldlist = oldlist->next;
w->next = newlist;
newlist = w;
}
return newlist;
}

有了上面,从标准输入中读取宽单词的程序基本上是

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
/* Functions listed above */
int main(void)
{
struct word  *list, *node;
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: Your C library does not support your current locale.n");
if (fwide(stdin, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard input.n");
if (fwide(stdout, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard output.n");
/* Read words from standard input into reversed list. */
while (1) {
node = wide_word(stdin);
if (!node) {
if (errno) {
fprintf(stderr, "Error reading standard input: %s.n", strerror(errno));
exit(EXIT_FAILURE);
}
/* No error, just end of input. */
break;
}
/* Prepend to list. */
node->next = list;
list = node;
}
/* Reverse the list so first word is first in list. */
list = reverse_list(list);
/* Print each word in the list to standard output, in order. */
for (node = list; node != NULL; node = node->next)
wprintf(L"%lsn", node->data);
/* We could free() each word in 'list' here. */
return EXIT_SUCCESS;
}

相关内容

最新更新