在动态分配内存时将文件数据读取到结构数组

时间:2014-07-19 05:46:14

标签: c memory-management dynamic-memory-allocation

我需要帮助解决下面的代码。在使用gcc编译代码之后,它可以像./compiledFile inputFile.txt一样运行它应该在输入文件时使用inputFile.txt读取它,同时为每个变量动态分配内存,在本例中为name和courseID,但我的代码不起作用。我不太了解并需要帮助的地方是分配内存,将数据插入结构并打印数据,如下面给出的示例。通过这段代码你可以看出我是c的新手以及动态内存分配和结构。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct people
{
  char* name[10];
  char* courseID[15];
  int grade;
};

void printData(struct people student[], int count);
int main(int argc, char* argv[])
{
  FILE *in_file;
  char buffer[30];
  char *token, *del=",";
  int count=0;

  struct people student[20];

  if(( in_file = fopen(argv[1], "r")) == NULL)
  {
     printf("unable to open the file");
  }
  while (fgets(buffer, sizeof(buffer), in_file))
  {
     student = malloc(sizeof(struct people));
     token = strtok(buffer, del);
     strcpy(student[count].name, token);
     count++;
  }
  fclose(in_file);
  printData(student, count);
}
void printData(struct people student[], int count)
{
  int i;
  for(i=0; i<count; i++)
  {
    printf("%s", student[i].courseID);
    if (strcmp((student[i].name, student[i].courseID) > 0))
    {
      printf("%s  %s", student[i].name, student[i].grade)
    }
  }
}

data.txt文件具有由comman分隔的以下内容:

John,MATH 1324,90 David,SCI 1401,88 Omondi,MATH 1324,89 David,MATH 1324,90

打印出来时应如下所示:

MATH 1324
John 90
Omondi 89
David 90

SCI 1401
David 88

4 个答案:

答案 0 :(得分:2)

首先,如果您还可以分享运行此程序时获得的实际输出或错误,那就太棒了。

大多数情况下,当我们不知道数据元素的实际大小时使用动态内存分配,但是在这里你已经修改了struct people student的大小为20

 struct people student[20];

这绝对没问题,但是你在while循环中做了malloc

     student = malloc(sizeof(struct student);

你已经使用数组声明分配了20个位置,现在不需要malloc。 如果你想使用指针进行学习目的的动态内存分配,那么你应该首先将student声明为指向struct struct的指针

struct people* student;

在while循环中动态分配内存

student=(struct people*) malloc(sizeof(struct people));

然后访问它

*(student+count)

希望这有帮助,如果您仍有疑问/问题编辑问题并包含编译/运行此程序时得到的输出/错误。

答案 1 :(得分:1)

问题代码的几个问题......

1)main()的定义:

int main(int argc, char* argv[])

必须返回一个整数。在main()的末尾添加一个return语句,然后做一个正确的&#34; CLEANUP&#34;部分:

   printData(student, count);

CLEANUP:

   if(in_file)
      fclose(in_file);

   return(0);
}

2)更好地处理fopen()错误条件:

  if(( in_file = fopen(argv[1], "r")) == NULL)
  {
     printf("unable to open the file");
     goto CLEANUP;
  }

并初始化in_file指针:

int main(int argc, char* argv[])
{
  FILE *in_file = NULL;

3)接下来,需要确定student的确切定义。它是静态数组,还是指向动态分配数组的指针?我假设您想使用动态数组(给出问题文本)。但是,此假设与以下行冲突,后者将student定义为静态数组:

  struct people student[20];

将其更改为:

  struct people *student = NULL;

4)现在,以下问题代码为每个学生分配一个新的(单独的)内存块:

     student = malloc(sizeof(struct people));

然而,所需要的是一个阵列中的所有学生记录,在同一块内存中。因此,我们需要的是扩展一块内存,以便在阅读时包含学生记录,如下所示:

  while (fgets(buffer, sizeof(buffer), in_file))
  {
     void *tmp = realloc(student, sizeof(struct people) * (count + 1));
     if(NULL == tmp)
     {
        printf("realloc() failed.\n");
        goto CLEANUP;
     }
     student = tmp;

     token = strtok(buffer, del);

5)看看人员结构:

struct people
{
  char* name[10];
  char* courseID[15];
  int grade;
};

对于指针而言,问题代码似乎有些困难。阵列。代码试图将name和courseID字段定义为指针和数组。鉴于问题与动态分配东西有关,我选择朝那个方向发展。因此,此结构应更改为以下内容:

struct people
{
  char *name;
  char *courseID;
  int   grade;
};

6)因此,每次循环时,学生姓名将被放置在已分配的存储空间中,并由.name字段指向。所以,改变这个:

     token = strtok(buffer, del);
     strcpy(student[count]->name, token);
     count++;
  }

到此:

     token = strtok(buffer, del);
     student[count].name = strdup(token);
     count++;
  }

7)我不明白这条线的意图:

   if (strcmp((student[i].name, student[i].courseID) > 0))

我倾向于消除它。


8)以下行有缺陷:

      printf("%s  %s", student[i].name, student[i].grade)

将其更改为打印整数grade(并且不要忘记结束分号):

      printf("%s  %d\n", student[i].name, student[i].grade);

&#39; \ n&#39;使输出看起来更好,每行一条记录。


9)由于student是指向动态分配的内存(不是静态数组)的指针,因此请更改:

void printData(struct people student[], int count)

到此:

void printData(struct people *student, int count)

10)现在,完成解析数据的任务;从这个:

     token = strtok(buffer, del);
     strcpy(student[count].name, token);
     count++;
  }

到此:

     token = strtok(buffer, del);
     student[count].name = strdup(token);
     token = strtok(NULL, del);
     student[count].courseID = strdup(token);
     token = strtok(NULL, del);
     student[count].grade = strtol(token, NULL, 10);
     count++;
  }

11)现在,为了让生活更轻松,请对数组进行排序。首先按courseID,然后按名称:

 ...  
     count++;
  }

  /** Sort the array by coursID, then by name. **/
  qsort(student, count, sizeof(*student), CmpStudentRecs);

  printData(student, count);
 ...

这将需要额外的&#34;比较学生成绩&#34;功能:

int CmpStudentRecs(const void *recA, const void *recB)
{
  int result = 0;
  struct people *stuRecA = (struct people *)recA;
  struct people *stuRecB = (struct people *)recB;

  /** First compare the courseIDs **/    
  result=strcmp(stuRecA->courseID, stuRecB->courseID);

  /** Second (if courseIDs match) compare the names **/
  if(!result)
     result=strcmp(stuRecA->name, stuRecB->name);

  return(result);
}

12)使用printData()函数进行一些最后润色:

void printData(struct people *student, int count)
{
  int i;
  char *courseID = "";

  for(i=0; i<count; i++)
  {
    if(strcmp(courseID, student[i].courseID))
      {
      printf("%s\n", student[i].courseID);
      courseID = student[i].courseID;
      }

    printf("\t%s  %d\n", student[i].name, student[i].grade);
  }
}

成品。输出:

SLES11SP2:~/SO> ./test data.txt
MATH 1324
   David  90
   John  90
   Omondi  89
SCI 1401
   David  88
SLES11SP2:~/SO> 

SPOILER

答案 2 :(得分:0)

  1. people的定义更改为:

    struct people
    {
      char name[10];
      char courseID[15];
      int grade;
    };
    

    这假设name不会超过9个字符,coursID不会超过14个字符。如果不是这样,请相应地更改它们。

  2. 该行:

    student = malloc(sizeof(struct student);
    

    在某些方面是错误的。

    • student已被声明为people的数组。您无法将其指定为指向由malloc分配的内存。

    • struct student不是类型。

    该行可以删除。

  3. 该行

    strcpy(student[count].name, token);
    
    如果token的长度超过10(或您在name中为people选择的任何尺寸),则

    会导致问题。更安全的事情是使用strncpy

    strncpy(student[count].name, token, 10);
    student[count].name[9] = '\0';
    
  4. 您尚未在任何地方设置courseID的值。但是,您正试图在printData中打印它。你正在为grade做同样的事情。您需要更新输入行的处理以正确设置它们。

    while循环更改为:

    while (fgets(buffer, sizeof(buffer), in_file))
    {
       token = strtok(buffer, del);
       strncpy(student[count].name, token, 10);
       student[count].name[9] = '\0';
       token = strtok(NULL, del);
       strncpy(student[count].courseID, token, 15);
       student[count].courseID[14] = '\0';
       token = strtok(NULL, del);
       student[count].grade = atoi(token);
       count++;
    }
    
  5. printData中有几个语法错误。但是,修复这些语法错误并不能满足您的打印要求。如果对数据进行排序,将按照您希望的顺序打印数据会更容易。以下功能将帮助您对数据进行排序。

    int compareStudent(const void* ptr1, const void* ptr2)
    {
       struct people* p1 = (struct people*)ptr1;
       struct people* p2 = (struct people*)ptr2;
       return (strcmp(p1->courseID, p2->courseID));
    }
    
    void sortData(struct people student[], int count)
    {
       qsort(student, count, sizeof(struct people), compareStudent);
    }
    

    您可以在致电sortData之前致电printData或先致电sortData致电printData。打印数据的逻辑需要稍微更新一下。这是一个更新的printData

    void printData(struct people student[], int count)
    {
      int i;
      int j;
    
      sortData(student, count);
    
      for(i=0; i<count; ++i)
      {
        printf("%s\n", student[i].courseID);
        printf("%s %d\n", student[i].name, student[i].grade);
        for ( j = i+1; j < count; ++j )
        {
           if (strcmp(student[i].courseID, student[j].courseID) == 0)
           {
              printf("%s %d\n", student[j].name, student[j].grade);
           }
           else
           {
              i = j-1;
              break;
           }
        }
      }
    }
    

答案 3 :(得分:0)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct people {
    char name[10];//char *name[10] is array of pointer 
    char courseID[15];
    int grade;
};

void printData(struct people student[], int count);

int main(int argc, char* argv[]){
    FILE *in_file;
    char buffer[30];
    char *token, *del=",";
    int count=0;

    struct people student[20];

    if((in_file = fopen(argv[1], "r")) == NULL){
        printf("unable to open the file");
        return -1;//It is not possible to continue the process
    }

    while (fgets(buffer, sizeof(buffer), in_file)){
        //student = malloc(sizeof(struct people));//It is by securing an array already
        token = strtok(buffer, del);
        strcpy(student[count].name, token);
        token = strtok(NULL, del);
        strcpy(student[count].courseID, token);
        token = strtok(NULL, del);
        student[count].grade = atoi(token);
        count++;
    }
    fclose(in_file);
    printData(student, count);
}

int cmp(const void *a, const void *b){
    const char *x = ((const struct people*)a)->courseID;
    const char *y = ((const struct people*)b)->courseID;
    return strcmp(x, y);
}

void printData(struct people student[], int count){
    qsort(student, count, sizeof(struct people), cmp);//sort by courseID
    char *prev = "";
    int i;
    for(i=0; i<count; i++){
        if(strcmp(prev, student[i].courseID)!=0){
            prev = student[i].courseID;
            printf("\n%s\n", prev);
        }
        printf("%-9s %d\n", student[i].name, student[i].grade);
    }
}