Какой будет лучший способ заполнить эту структуру? - PullRequest
1 голос
/ 23 ноября 2010

Для выполнения домашнего задания мне нужно заполнить список студенческих структур, который выглядит следующим образом:

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

Фактические данные должны быть прочитаны из файла .csv и выглядят примерно как

2345678; Мейер; Ганс; 12.10.1985; 2,4

1234567; Мюллер; Фриц; 17.05.1990; 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()

Ответы [ 4 ]

2 голосов
/ 23 ноября 2010

Я бы пошел в следующем порядке:

  • сначала прочитайте всю строку
  • , затем проверьте правильность номера поля (подсчет ; должен подойти для вашего примера) и обработать ошибку (пропустить строку или прекратить анализ?)
  • , а затем разбить строку на char*[] (вы можете сделать это на месте, поместив '\0' и используя непосредственно строку или создав новые строки)
  • затем проверьте правильность обязательных полей (зачисление - это число, день рождения - это дата и т. Д.)
  • затем заполните реальную структуру (вы можете использовать strcpy, strdup илиКопирование указателя на строку в соответствии с вашими потребностями)
1 голос
/ 23 ноября 2010

Поскольку в 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
         */
    }   

0 голосов
/ 23 ноября 2010

Я бы прочитал каждую строку CSV, а затем сохранил ее в структуре Student.

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 );
}

}

Таким образом, вы читаете содержимое в векторе строк. Вектор строк инициализируется пустой строкой, поэтому не возникает проблем, если одно поле пустое или отсутствует.

После прочтения строки вы можете сохранить ее в структуре Student:

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 ] );
}

Примите во внимание, что некоторые шаги отсутствуют. Но этот скелет должен дать вам хорошую отправную точку.

Надеюсь, это поможет.

0 голосов
/ 23 ноября 2010

Инициализировать поля указателя в структуре для нулевых указателей;как отмечено в комментариях, memset здесь не является правильным вариантом - используйте способ c99 или делайте это явно для каждого поля.

Если чтение поля не удается по какой-либо причине, вам следует освободить уже выделенные поля.Например, если чтение average не удается для студента, и вы решили игнорировать запись этого студента, вы должны освободить его поля name, чтобы предотвратить утечки памяти (конечно, при условии, что они распределены неправильно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...