我的代码又遇到了麻烦。
我想fscanf
结果.txt到带有链表的结构,但它不起作用;我认为简单的链表一定足够了;
问题是:程序只写第一行,但没有别的。
结果.txt格式:
point name (for examples)
623 john
457 peter
312 chuck
etc.
代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct ranklist {
int point;
char* name;
struct ranklist *next;
} ranklist;
int how_many_records(FILE *fp){
char ch;
int line=0;
int status;
rewind(fp);
while((status=fscanf(fp, "%*d %*[^n]%c", &ch))==1)
++line;
if(status != EOF){
++line;
}
rewind(fp);
return line;
}
int how_many_letter(FILE *fp){
int letter = 0;
long pos = ftell(fp);
//fscanf(fp, " %*[^n]%n", &letter);
fscanf(fp, " %*s%n", &letter);
fseek(fp, pos, SEEK_SET);
return letter;
}
int main(void){
FILE *fp = fopen("result.txt","r");
int name_length;
int lines = how_many_records(fp);
ranklist *r = malloc(lines * sizeof(*r));
ranklist *first = r;
for ( r=first ;r != NULL; r = r->next){
fscanf(fp, "%d", &(r->point));
name_length = how_many_letter(fp);
r->name = malloc(name_length + 1);
fscanf(fp,"%s", r->name);
}
fclose(fp);
for ( r=first ;r != NULL; r = r->next){
printf("%d %sn", r->point, r->name);
}
free(r);
return 0;
}
fscanf(fp, "%d", &r[y].point);
在这里,y
是未初始化的。
你需要把y = 0
,或者,IMO,更好地使用&(r->point)
[r[y].name
也是如此]
建议:用于读取和解析整行更好地使用fgets()
创建列表时遇到多个问题。
让我们从循环开始:
for ( r=first ;r != NULL; r = r->next){
在循环中没有任何地方初始化r->next
,所以在第一次迭代之后,你会y
指向完全随机的内存,导致未定义的行为。
如另一个答案所述,您不初始化变量y
,这是未定义行为的另一个原因。
您还更改了r
,因此r
不会指向您分配的原始内存,因此这样做r[y]
是未定义行为的第三个原因。
在循环之后,你用修改后的指针y
调用free
,这也会导致未定义的行为。而且您不会释放在循环中分配的名称,因此也会有多个内存泄漏。
根本不需要预先分配节点,只需在需要时分配即可。
类似的东西
ranklist *head = NULL;
FILE *fp = fopen(...);
// Read the file and create the list
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp) != NULL)
{
ranklist *node = malloc(sizeof(ranklist));
node->name = malloc(strlen(buffer)); // Will allocate a little more than needed
sscanf(buffer, "%d %s", &node->point, node->name);
node->next = head;
head = node;
}
// Print the list
for (ranklist *n = head; n != NULL; n = n->next)
{
printf("%d %sn", n->point, n->name);
}
// Free the list
while (head != NULL)
{
// Unlink the first node in the list
ranklist *n = head;
head = n->next;
// And free it
free(n->name);
free(n);
}
上面的代码没有任何错误检查,在为名称分配空间时也有一些开销,但它是安全的,它不会处理不切实际的长名称,并且列表实际上将是一个堆栈(最后读取的项目将是列表中的第一个(。
从用fgets
读取的缓冲区中获取名称长度的函数:
size_t get_name_length(char *buffer)
{
// Since the buffer was read with `fgets` we need to get rid
// of the newline at the end, if it's there
size_t length = strlen(buffer);
if (buffer[length - 1] == 'n')
buffer[length - 1] = ' '; // "Remove" by terminating the string
// Find the space dividing the point and the name
char *space = strchr(buffer, ' ');
// Just in case there are multiple whitespace characters in the string
while (*space != ' ' && isspace(*space))
++space;
// Now `space` points to the first non-space letter in the name
// (or to the string terminator, if there's no name
// Then length of the name is the remainder of the string
return strlen(space);
}
FILE *fp = fopen("result.txt","r");
int name_length;
//Just create a link if you use as an array
//int lines = how_many_records(fp);
//ranklist *r = malloc(lines * sizeof(*r));
ranklist *first=NULL, *curr, *r;
int point;
while(1==fscanf(fp, "%d", &point)){
r = malloc(sizeof(*r));//allocate one record
r->next = NULL;
r->point = point;
name_length = how_many_letter(fp);
r->name = malloc(name_length + 1);
fscanf(fp, "%s", r->name);
//make link
if(first == NULL)
first = curr = r;
else
curr = curr->next = r;
}
fclose(fp);
for (r = first; r != NULL; r = r->next){
printf("%d %sn", r->point, r->name);
}
for (r = first; r != NULL; ){
ranklist *tmp = r->next;//Save next for free(r)
free(r->name);
free(r);
r = tmp;
}
//output the list
void print(ranklist *first){
while(first != NULL){
printf("%d %sn", first->point, first->name);
first = first->next;
}
}