Как загрузить несколько «клонов» структуры из FILE? С - PullRequest
0 голосов
/ 05 сентября 2018

Я хочу узнать, как загрузить несколько структур (много студентов: имя, фамилия, индекс, адрес ...) из текстового файла , похожего на:

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='\0';//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 ), однако, чтобы загрузить несколько, у меня есть идея, но я ' Я не уверен, слишком ли это неуклюже для работы или просто ужасно написано.

Позже я попытался бы отсортировать студентов по имени и, возможно, даже попытаться создать буфер realloc, который увеличивает его размер вместе с новыми студентами, загружаемыми в буфер ... и затем сортировать что ' d был загружен ... но я представляю, что сначала мне нужно загрузить его из файла в буфер и из буфера для заполнения структуры, чтобы потом можно было его отсортировать? ...

Большое спасибо за помощь!

1 Ответ

0 голосов
/ 05 сентября 2018

С немного резковато. Я использую 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] = '\0';

        // 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 != '\0') {
            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;
}

Для следующего ввода в stdin:

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

Доступна тестовая версия здесь, на сайте gdb .

...