我很抱歉这样做的问题(因为互联网上有很多这样的问题),但我不得不问这个问题:
练习涉及从带有学生列表的文件中读取(记录包含:姓名,姓氏和序列号)。我已经创建了文档,由13行组成,但是当我在终端./a.out
上写字时,输出是这类13行的列表:(null) (null) (null)
代码是:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define EOF (-1)
#define BUF 100
typedef struct stud{
char *surname;
char *name;
char *serial;
} student;
int main(void){
FILE *fd;
int n = BUF;
int k = 0;
int i = 0;
int ret;
char *s = malloc(BUF * sizeof(char));
if((fd = fopen("registry_office_students.txt","r")) == NULL){
perror("error opening file");
return -1;
}
while(fgets(s,n,fd)!=NULL){
k++;
}
student *a = malloc(k*sizeof(student));
rewind(fd);
ret = fscanf(fd, "%s, %s, %s", a[i].surname, a[i].name, a[i].serial);
while(fscanf(fd, "%s, %s, %s", a[i].surname, a[i].name, a[i].serial) == ret){
i++;
}
for(i=0;i<k;i++){
printf("%s, %s, %s \n", a[i].surname, a[i].name, a[i].serial);
}
fclose(fd);
return 0;
}
我再次道歉并希望得到适当的回应,谢谢。
答案 0 :(得分:5)
%s
的 fscanf(3)
不会为字符串分配任何内存。该字符串应该已经存在。
至少,替换
ret = fscanf(fd, "%s, %s, %s",
a[i].surname, a[i].name, a[i].serial);
类似
{
char surname[48];
char name[64];
char serial[32];
memset (surname, 0, sizeof(surname));
memset (name, 0, sizeof(name));
memset (serial, 0, sizeof(serial));
memset (a+i, 0, sizeof(struct stud));
ret = fscanf(fd, "%47s, %63s, %31s", surname, name, serial);
if (ret==3) {
a[i].surname = strdup(surname);
if (!a[i].surname)
{ perror("strdup surname"); exit(EXIT_FAILURE); }
a[i].name = strdup(name);
if (!a[i].name)
{ perror("strdup name"); exit(EXIT_FAILURE); }
a[i].serial = strdup(serial);
if (!a[i].serial)
{ perror("strdup serial"); exit(EXIT_FAILURE); }
}
}
请注意我在阅读之前清理内存。我明确地给出了fscanf
格式的字符串大小。我正在使用测试 strdup
将读取的字符串复制到堆中。
实际上,我相信你的做法可能是错的。您可以决定每个学生应该在一行上,您可以使用getline(3)阅读并使用sscanf(3)进行解析(也许%n
会有用!)或者strtok
(或“手动”使用isalpha
)
请阅读更多关于C编程的资料,然后编译所有警告和调试信息(gcc -Wall -g
),学会使用调试器(gdb
)和内存泄漏检测器({{3} })。
答案 1 :(得分:2)
首先,您永远不会为结构中存储的字符串分配内存。即您的fscanf
尝试将数据读入不存在的缓冲区。
其次,您的阅读代码将数据读入a[0]
两次。即第一个fscanf
将读取a[0]
中的第一条记录,然后下一个fscanf
将再次将下一条记录读入a[0]
,覆盖之前读取的内容。为什么?那是你的意图(比如跳过表格标题或类似的东西)?
第三,您的点钞代码(fgets
)与您的阅读代码(fscanf
)不同。如果阅读代码因fscanf
特定原因而过早失败,您将阅读少于k
条记录。然而,您的打印代码无条件地打印所有k
个打印代码。 (如果由于fscanf
格式的某些错误导致您的阅读代码立即失败,该怎么办?在这种情况下,您从未阅读任何内容。)
第四,计数代码中对fgets
的每次调用均受100
字符或换行符限制(fgets
的工作方式)。这与fscanf
的工作方式完全不同步,不受您案例中任何内容的限制。这意味着计数代码所看到的记录数量可能很容易与读取代码所看到的记录数量不同(更大)。