2014-09-28 93 views
2

返回值不好意思做这种问题(因为有关于互联网上这么多),但我不得不问这样的:问题的fscanf的在C

演习涉及从阅读文件与学生名单(记录包含:姓名,序号)。我已经创建的文档,并包括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

您是否仔细阅读了几次[fscanf(3)]的文档(http://man7.org/linux/man-pages/man3/fscanf.3.html)?问题是什么? – 2014-09-28 15:49:24

+0

@JeroenvanderHooft上面的代码是有效的。它允许Carmine将该结构称为“struct stud”或“student”。 – 2014-09-28 15:53:23

回答

5

fscanf(3)%s不会为字符串分配任何内存。该字符串应该已经存在。

至少,东西取代

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)和内存泄漏检测器(valgrind)。

2

首先,你永远不会为存储在结构中的字符串分配内存。即您的fscanf尝试将数据读入不存在的缓冲区中。

其次,您的读取代码将数据读取到a[0]两次。即第一个fscanf将读取a[0]中的第一条记录,然后下一个fscanf将再次读取下一条记录到a[0],覆盖之前读取的内容。为什么?这是你的意图(就像跳过表头或类似的东西)?

第三,您的计数代码(fgets)与您的阅读代码(fscanf)不一样。如果读码因为fscanf特定的原因过早失败,您将读取的记录少于k。然而,您的打印代码无条件打印所有k。 (如果你的阅读代码fscanf格式立即失败,由于一些错误?在这种情况下,你从来不看任何东西。)

第四,在你的计数的代码每次调用fgets100字符或一个换行符的限制(也就是fgets如何工作)。这与fscanf的工作方式完全不同步,您的情况不受任何限制。这意味着计数代码看到的记录数可能很容易与读代码看到的记录数不同(大于)。