如何从 FILE 加载多个 "clones" 结构? C

How to load multiple "clones" of structure from FILE? C

我想学习如何从 文本文件 中加载多个结构(许多学生:名字、姓氏、索引、地址...),如下所示:

Achilles, 9999
Hector, 9998
Menelaos, 9997
... and so on

结构可以是这样的:

struct student_t {
    char *name;
    int index;
}

我的尝试(没有用;我什至不确定 fgets+sscanf 在这里是否是一个不错的选择):

int numStudents=3; //to simplify... I'd need a function to count num of lines, I imagine
int x, y=1000, err_code=1;

FILE *pfile = fopen("file.txt", "r");
if(pfile==0) {return 2;}

STUDENT* students = malloc(numStudents * sizeof *students);

char buffer[1024];
char *ptr[numStudents];
for (x = 0; x < numStudents; x++){ //loop for each student
    students[x].name=malloc(100); //allocation of each *name field 
    fgets(buffer, 100, pfile); //reads 1 line containing data of 1 student, to buffer
    if(x==0) *ptr[x] = strtok(buffer, ",");//cuts buffer into tokens: ptr[x] for *name
    else *ptr[x] = strtok(NULL, ","); //cuts next part of buffer
    sscanf(ptr[x], "%19s", students[x].name); //loads the token to struct field
    *ptr[y] = strtok(NULL, ","); //cuts next part of the buffer
    students[y].index = (int)strtol(ptr[y], NULL, 10); //loads int token to struct field
    *buffer='[=13=]';//resets buffer to the beginning for the next line from x++ fgets...
    y++;//the idea with y=1000 is that I need another pointer to each struct field right?
}

for (x = 0; x < numStudents; x++)
    printf("first name: %s, index: %d\n",students[x].name, students[x].index);

return students;

然后打印它以查看加载的内容。 (为了简化我有 6 个字段的真实结构)。我知道一种从用户输入加载 1 名学生的好方法...() 但是要加载多个学生,我有这个想法,但我不确定它是否太笨拙而无法工作或只是写得很乱。

稍后我会尝试按姓名对学生进行排序,甚至可能会尝试做一个重新分配缓冲区,随着新学生被加载到缓冲区来增加它的大小......然后对加载的内容进行排序...但我想首先我需要将它从文件加载到缓冲区,然后从缓冲区加载到填充结构,然后才能对其进行排序?...

非常感谢您的帮助!

C有点苛刻。我在下面使用 GNU getline,它可能不可移植,您最终可能会自己实现。为了简单起见,我使用 stdin 作为输入 FILE *
该程序将学生列表读入 students 数组。然后我通过比较索引对学生进行排序,然后按姓名排序,每次都打印出来。
您的代码有点混乱 - 尝试编写一个单独的函数来加载单个学生,您不需要 char ptr[students] 只需要一个 char *ptr 用于 strtok 函数。 strtok 有点混乱,我更喜欢只使用 strchr 多次。我使用 memcpy 只是从字符串中复制名称并记住将其设为空定界符。

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>

struct student_s {
    char *name;
    int index;
};

static int students_name_cmp(const void *a, const void *b)
{
    const struct student_s *s1 = a;
    const struct student_s *s2 = b;
    return strcmp(s1->name, s2->name);
}

static int students_index_cmp(const void *a, const void *b)
{
    const struct student_s *s1 = a;
    const struct student_s *s2 = b;
    return s1->index - s2->index;
}

int main()
{
    struct student_s *students = NULL;
    size_t students_cnt = 0;
    FILE *fp = stdin;
    size_t read;
    char *line = NULL;
    size_t len = 0;

    // for each line
    while ((read = getline(&line, &len, fp)) != -1) {

        // resize students!
        students = realloc(students, (students_cnt + 1) * sizeof(*students));
        // handle erros            
        if (students == NULL) {
            fprintf(stderr, "ERROR allocating students!\n");
            exit(-1);
        }

        // find the comma in the line
        const const char * const commapos = strchr(line, ',');
        if (commapos == NULL) {
            fprintf(stderr, "ERROR file is badly formatted!\n");
            exit(-1);
        }
        // student has the neme between the start to the comma adding null delimeter
        const size_t namelen = (commapos - line) + 1;
        // alloc memory for the name and copy it and null delimeter it
        students[students_cnt].name = malloc(namelen * sizeof(char));
        // handle errors
        if (students[students_cnt].name == NULL) {
             fprintf(stderr, "ERROR allocating students name!\n");
             exit(-1);
        }
        memcpy(students[students_cnt].name, line, namelen - 1);
        students[students_cnt].name[namelen] = '[=10=]';

        // convert the string after the comma to the number
        // strtol (sadly) discards whitespaces before it, but in this case it's lucky
        // we can start after the comma
        errno = 0;
        char *endptr;
        const long int tmp = strtol(&line[namelen], &endptr, 10);
        // handle strtol errors
        if (errno) {
            fprintf(stderr, "ERROR converting student index into number\n");
            exit(-1);
        }
        // handle out of range values, I use INT_MIN/MAX cause index is int, no better idea, depends on application
        if (tmp <= INT_MIN || INT_MAX <= tmp) {
            fprintf(stderr, "ERROR index number is out of allowed range\n");
            exit(-1);
        }
        students[students_cnt].index = tmp;

        // handle the case when the line consist of any more characters then a string and a number
        if (*endptr != '\n' && *endptr != '[=10=]') {
            fprintf(stderr, "ERROR there are some rabbish characters after the index!");
            exit(-1);
        }

        // finnally, increment students count
        students_cnt++;
    }
    if (line) {
        free(line);
    }

    // sort by index
    qsort(students, students_cnt, sizeof(*students), students_index_cmp);

    // print students out sorted by index
    printf("Students sorted by index:\n");
    for (size_t i = 0; i < students_cnt; ++i) {
        printf("student[%zu] = '%s', %d\n", i, students[i].name, students[i].index);
    }

    // now we have students. We can sort them.
    qsort(students, students_cnt, sizeof(*students), students_name_cmp);

    // print students out sorted by name
    printf("Students sorted by name:\n");
    for (size_t i = 0; i < students_cnt; ++i) {
        printf("student[%zu] = '%s', %d\n", i, students[i].name, students[i].index);
    }

    // free students, lucky them!
    for (size_t i = 0; i < students_cnt; ++i) {
        free(students[i].name);
    }
    free(students);

    return 0;
}

对于标准输入的以下输入:

Achilles, 9999
Hector, 9998
Menelaos, 9997

程序输出:

Students sorted by index:
student[0] = 'Menelaos', 9997
student[1] = 'Hector', 9998
student[2] = 'Achilles', 9999
Students sorted by name:
student[0] = 'Achilles', 9999
student[1] = 'Hector', 9998
student[2] = 'Menelaos', 9997

可用的测试版本 here on onlinegdb