如果您仍然在为问题苦苦挣扎,那么您已经不远了,但是您可以根据您提供的示例数据文件来更轻松地读取和解析信息。如果你看文件,你只关心读取最后一行数据并将其分离成单词。查看前面的所有行,它们都以标点符号(# 或 %)开头,而最后一行以字母字符开头。
虽然有很多方法可以做到这一点,但一种非常有效的方法是使用fgets(或POSIX getline)简单地将每一行读入一个固定缓冲区(比如word),然后使用工具从<ctype.h> 测试第一个字符是ispunct() 还是isdigit()。如果任一测试true,只需阅读下一行。这种方法的简单性意味着当您退出读取循环时,您的读取缓冲区中包含最后一行。一个简单的实现是:
#define MAXLETTERS 256
...
char word[MAXLETTERS] = "", /* fixed buffer to hold each line */
*p = word, /* pointer to with for strtok */
*delim = " \t\n"; /* delimiters to use with strtok */
...
while (fgets (word, MAXLETTERS, stdin)) /* read/discard until last line */
if (ispunct (*word) || isdigit (*word))
continue;
else
break;
使用word 中包含的行,您可以使用strtok 根据您指定的任何分隔符(' ' 和'\n')将行分隔为单个单词,这在此处是有意义的。 strtok 返回一个指向每个单词开头的指针,并且在每次连续调用时,将指向该行中的下一个单词。您对strtok 的第一次调用使用包含您的行的缓冲区的名称,例如
char word[MAXLETTERS] = "", /* fixed buffer to hold each line */
...
p = strtok (p, delim); /* 1st call to strtok for 1st word */
每个连续调用都使用NULL 代替buf,例如
p = strtok (NULL, delim); /* all subsequent calls use NULL */
当strtok到达原始字符串的末尾时,它会返回NULL。
(注意: strtok 通过在对字符串进行标记时插入 '\0' 字符来修改字符串——因此,如果您需要维护原始字符串,请复制原始字符串)
然后,您只需将每个标记(单个单词)传递给您的 insert_at_foot (list, p) 函数。您可以将所有步骤组合成一个简单的for 循环,如下所示:
/* tokenize last line using strtok */
for (p = strtok (p, delim); p; p = strtok (NULL, delim))
insert_at_foot (list, p); /* insert word in llqueue */
在insert_at_foot () 中,您不能分配 字符串。如 cmets 中所述,问题的一个潜在来源是您键入了一个数组,该数组掩盖了函数中 word 的类型。它只是char*,你必须使用strcpy复制到new->word(不是new->word = word;)
修复该问题并整理函数并为list 添加验证检查,您可以执行以下操作:
list_t *insert_at_foot (list_t *list, char *word)
{
node_t *new = malloc(sizeof *new);
assert (list != NULL && new != NULL); /* validate both list & node */
strcpy (new->word, word); /* you cannot assign strings, strcpy */
new->next = NULL; /* initialize next to NULL */
if (list->foot==NULL) { /* check if list is empty */
/* this is the first insertion into the list */
list->head = list->foot = new;
}
else { /* additional nodes added at foot */
list->foot->next = new;
list->foot = new;
}
return list;
}
总而言之(并填写您在帖子中未提供的功能),一个工作示例可能类似于:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#define MAXLETTERS 256
typedef struct node node_t;
/* listops.c */
struct node {
char word[MAXLETTERS]; //dereferences the first letter in data_t[MAXLETTERS]
node_t *next;
};
typedef struct {
node_t *head;
node_t *foot;
} list_t;
list_t *insert_at_foot (list_t *list, char *word);
int is_empty_list (node_t *thenode)
{
return thenode == NULL;
}
int main (void) {
char word[MAXLETTERS] = "",
*p = word,
*delim = " \t\n";
list_t *list = calloc (1, sizeof *list); /* allocate list */
while (fgets (word, MAXLETTERS, stdin)) /* read/discard until last line */
if (ispunct (*word) || isdigit (*word))
continue;
else
break;
/* tokenize last line using strtok */
for (p = strtok (p, delim); p; p = strtok (NULL, delim))
insert_at_foot (list, p); /* insert word in llqueue */
// print_list(list);
node_t *iter = list->head; /* temp node to iterate over list */
while (!is_empty_list(iter)) { /* while node not NULL */
node_t *victim = iter; /* temp node to free */
printf("%s\n", iter->word); /* output word saved in node */
iter = iter->next; /* set iter to next node */
free (victim); /* free current node */
}
free (list); /* don't forget to free the list */
}
list_t *insert_at_foot (list_t *list, char *word)
{
node_t *new = malloc(sizeof *new);
assert (list != NULL && new != NULL); /* validate both list & node */
strcpy (new->word, word); /* you cannot assign strings, strcpy */
new->next = NULL; /* initialize next to NULL */
if (list->foot==NULL) { /* check if list is empty */
/* this is the first insertion into the list */
list->head = list->foot = new;
}
else { /* additional nodes added at foot */
list->foot->next = new;
list->foot = new;
}
return list;
}
输入文件示例
$ cat dat/llqueue.txt
#hewitt
5 95 0
#hugh
40 60 0
#jackman
0 100 0
#logan
40 0 60
#melbourne
5 5 90
#sydney
5 5 90
#zack
40 40 20
%%%%%%%%%%
hugh jackman is retiring the wolverine character after logan
使用/输出示例
$ ./bin/llqueue <dat/llqueue.txt
hugh
jackman
is
retiring
the
wolverine
character
after
logan
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。
对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/llqueue <dat/llqueue.txt
==22965== Memcheck, a memory error detector
==22965== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==22965== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==22965== Command: ./bin/llqueue
==22965==
hugh
jackman
is
retiring
the
wolverine
character
after
logan
==22965==
==22965== HEAP SUMMARY:
==22965== in use at exit: 0 bytes in 0 blocks
==22965== total heap usage: 10 allocs, 10 frees, 2,392 bytes allocated
==22965==
==22965== All heap blocks were freed -- no leaks are possible
==22965==
==22965== For counts of detected and suppressed errors, rerun with: -v
==22965== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放已分配的所有内存并且没有内存错误。
如果您还有其他问题或我以任何方式误解了您的问题,请查看并告诉我。