2010-11-23 33 views
1

对于功课assignement,我需要填写学生结构,看起来像下面的列表:这将是填充该结构的最佳方式?

struct Student { 
    int matriculationNumber; 
    char *firstName; 
    char *lastName; 
    char *birthday; 
    double averageGrage; 
} 

的实际数据必须从一个.csv文件中读取,看起来somethink像

2345678;梅耶尔;汉斯; 1985年12月10日; 2,4-

1234567;穆勒;弗里茨; 1990年5月17日; 1,9-

为了读取数据,应该使用fgetc()。现在

,问题是我怎么实际上填补了结构的领域,以及如何处理异常情况(即意外EOF;想起例如,如果一个行不包含字段birthday或场averageGroup)。

这是我会做它直观地(这是最有可能的方式不对;-)):

Student student; 

if (fillMatriculationNumber(&student, fp) == -1) { // return -1 on failure or EOF 
    goto failure; 
} 

if (fillFirstName(&student, fp) == -1) { 
    goto failure; 
} 

if (fillLastName(&student, fp) == -1) { 
    goto failure; 
} 

if (fillBirthday(&student, fp) == -1) { 
    goto failure; 
} 

if (fillAverageGrade(&student, fp) == -1) { 
    goto failure; 
} 

// OK 

:failure 
    // print a message about what's wrong, and exit() 
+0

我是C新手,但我认为`goto`语句多年没有使用过? – MrMisterMan 2010-11-23 14:15:04

+1

Imo,转发goto就像这些,如果使用负责任,实际上可以提高代码的可读性 - 太多的返回语句和/或高度嵌套和缩进代码是其他选择。 @MrM – Amarghosh 2010-11-23 14:24:19

+0

几乎可以肯定的是,你不应该使用goto来在代码中向后跳转。使用goto跳出循环或进行基本的异常处理是可以的,但应尽可能避免。 – onemasse 2010-11-23 15:46:55

回答

2

我会去按以下顺序:

  • 第一读取整个行
  • 然后检查字段数是否正确(计数为;应该对您的示例为好)并处理错误情况(跳过行或停止解析?)
  • then spl它在char*[](你可以通过放置'\0'并直接使用字符串或创建新字符串来实现)
  • 然后检查所需字段的正确性(预科是一个数字,生日是日期等)
  • 然后填写真实结构
+0

检查正确数量的字段是一个非常好的主意。我认为我坚持:-)。 – helpermethod 2010-11-23 14:20:13

+0

您能否详细说明您将如何分割线? – helpermethod 2010-11-27 18:03:40

0

(你可以根据自己的需要使用strcpystrdup或直接复制指针的字符串)初始化的结构,以空指针的指针字段;正如在注释中指出的那样,memset在这里不是正确的选项 - 使用c99方式或明确地为每个字段执行。

如果由于某种原因读取字段失败,则应释放已分配的字段。例如,如果读取average失败,并且您决定忽略该学生的记录,则应该释放其name字段以防止内存泄漏(当然,假设它们是malloc化的)。

0

我会读取每个CSV行,然后将其存储在学生结构中。

const unsigned int MaxFields = 5; 
const unsigned int MaxContents = 80; 

void readRow(FILE * f, char dataRow[MaxFields][MaxContents]) 
{ 
    int c; 
    unsigned int i; 
    char buffer[MaxContents]; 
    int pos; 
    int field; 

// Empty all fields 
for(i = 0; i < MaxFields; ++i) { 
    dataRow[ i ][ 0 ] = '\0'; 
} 

// Read rows 
buffer[ 0 ] = '\0'; 
c = fgetc(f); 
pos = 0; 
field = 0; 
while(c != EOF 
    && c != '\n') 
{ 
    if (c != ';') { 
     buffer[ pos++ ] = c; 
    } else { 
     buffer[ pos ] = '\0'; 
     strcpy(dataRow[ field++ ], buffer); 
     buffer[ 0 ] = '\0'; 
     pos = 0; 
    } 

    c = fgetc(f); 
} 

}

这样,你正在阅读的内容在字符串中的向量。字符串的矢量被初始化为空字符串,所以如果一个字段为空或缺失,则没有问题。

一旦行被读取,你可以将其存储在一个学生的结构:

char * safeStrDup(const char * src) 
{ 
    char * toret = strdup(src); 

    if (toret == NULL) { 
     fprintf(stderr, "Not enough memory\n"); 
     exit(EXIT_FAILURE); 
    } 

    return toret; 
} 

void store(Student *s, char dataRow[MaxFields][MaxContents]) 
{ 
    s->matriculationNumber = atoi(dataRow[ 0 ]); 
    s->firstName = safeStrDup(dataRow[ 1 ]); 
    s->lastName = safeStrDup(dataRow[ 2 ]); 
    s->birthday = safeStrDup(dataRow[ 3 ]); 
    s->averageGrage = atof(dataRow[ 4 ]); 
} 

考虑到,一些步骤缺少帐户。但是这个骨架应该给你一个很好的起点。

希望这会有所帮助。

1

由于fgetc()中存在压力,因此可以稍微更改代码。

 
    while(!feof(fp)) { 
     readRecordSuccess = 0; 
     if (fillMatriculationNumber(&student, fp) != -1) { // return -1 on failure or EOF 
      if (fillFirstName(&student, fp) != -1) { 
       if (fillLastName(&student, fp) != -1) { 
        if (fillBirthday(&student, fp) != -1) { 
         if (fillAverageGrade(&student, fp) != -1) { 
          readRecordSuccess = 1; 
         } 
        } 
       } 
      } 
     } 

     if(readRecordSuccess == 0) { 
      /* may clean already filled structure(s) */ 
      break; 
     } 

     /* 
     * the structure will be overwritten in the next iteration 
     * take proper measure 
     */ 
    } 

相关问题