Сохранение строк в структурах с помощью gets()?

У меня возникают некоторые проблемы при сохранении строки - например, имени - в поле структуры. Я использовал и gets(), и fgets(), но fgets() тоже не работает должным образом.

У меня никогда не было возможности ввести имя первого сотрудника; он переходит прямо к коду сотрудника, а затем также пропускает адрес. По какой-то причине при вводе второго сотрудника я ввожу и имя, и код, а затем снова пропускает адрес.

Кто-нибудь знает, что я делаю неправильно?

#include <stdio.h>

typedef struct {
    char name[150];
    int code;
    char add[300];
} tEmployee;

int main()
{
    printf("How many employees would you like to register?\n");
    int n;
    scanf("%i", &n);

    tEmployee employee[n];

    for (int i = 0; i < n; i++)
    {
        printf("Name: ");
        gets(employee[i].name);
        printf("Code: ");
        scanf("%i", &employee[i].code);
        printf("Address: ");
        gets(employee[i].add);

        printf("%s\n", employee[i].name);
        printf("%i\n", employee[i].code);
        printf("%s\n", employee[i].add);
     }
     return 0;
}

person Aryella Lacerda    schedule 13.05.2016    source источник
comment
Никогда не используйте gets().   -  person Fred Larson    schedule 13.05.2016
comment
Помните, как нажать две клавиши для scanfs? Номер и ввод? Enter генерирует \n. gets/fgets потребляет его, не дожидаясь дальнейшего ввода. Исправление? Добавляйте getchar(); после каждого scanf.   -  person Spikatrix    schedule 13.05.2016
comment
scanf сложно правильно использовать. Я бы предпочел использовать fgets во временном буфере, а затем atoi или sscanf результат, например Jonathan Leffler.   -  person Fred Larson    schedule 13.05.2016


Ответы (3)


Подпрограммы ввода из библиотеки C несовместимы в отношении того, как они обрабатывают символ новой строки (\n). Кто-то читает это как часть ввода, кто-то нет. Поскольку scanf() получает то, что ему нужно, перед новой строкой, у него нет причин читать его, поэтому мы должны явно очистить его из буфера перед нашим следующим вводом. Существуют разные методы, но в этом примере работает только вызов getchar().

Кроме того, поскольку gets() считается небезопасным и оставляет символ новой строки в конце ввода, я добавил пользовательскую оболочку my_gets(), которая устраняет обе проблемы:

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

typedef struct {
    char name[150];
    int code;
    char add[300];
} tEmployee;

char *my_gets(char *str, int size)
{
    char *pos;

    char *result = fgets(str, size, stdin);

    if (result != NULL && (pos = strchr(str, '\n')) != NULL)
        *pos = '\0';

    return result;
}

int main()
{
    int n;
    printf("How many employees would you like to register?\n");
    scanf("%i", &n);
    getchar(); // eat newline \n

    tEmployee employee[n];

    for (int i = 0; i < n; i++)
    {
        printf("Name: ");
        my_gets(employee[i].name, 150);

        printf("Code: ");
        scanf("%i", &employee[i].code);
        getchar(); // eat newline \n

        printf("Address: ");
        my_gets(employee[i].add, 300);

        printf("%s\n", employee[i].name);
        printf("%i\n", employee[i].code);
        printf("%s\n", employee[i].add);
    }

    return 0;
}

Вы можете сделать аналогичную функцию-оболочку для вашего конкретного использования scanf(), которая съедает лишнюю новую строку для вас, поэтому вам не нужно беспокоиться об этом каждый раз, когда вы вызываете эту функцию для ввода.

person cdlane    schedule 13.05.2016

Это ваше смешанное использование gets и scanf. Я столкнулся с аналогичной проблемой в С++, когда я смешал использование оператора std::cin и >> и функции std::getline.

Кроме того, gets устарело, не используйте его...

В любом случае, если вы действительно хотите использовать и то, и другое, то вам следует «сбрасывать» стандартный ввод каждый раз, когда вы используете scanf, или в следующий раз, когда вы будете читать стандартный ввод, вы будете читать остальную часть до конца строки (\n).

Один из способов сделать это — прочитать до конца строки после каждого scanf:

/* define the function */
void flush()
{
    while (getchar() != '\n');
}

Затем используйте его в своем коде следующим образом:

printf("How many employees would you like to register?\n");
int n;
scanf("%i", &n);

flush();

tEmployee employee[n];

for (int i = 0; i < n; i++)
{
    printf("Name: ");
    gets(employee[i].name);
    printf("Code: ");
    scanf("%i", &employee[i].code);

    flush();

    printf("Address: ");
    gets(employee[i].add);

    printf("%s\n", employee[i].name);
    printf("%i\n", employee[i].code);
    printf("%s\n", employee[i].add);
 }
 return 0;
person Eekan    schedule 13.05.2016

Попробуй это:

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

typedef struct {
    char name[150];
    int32_t code;
    char add[300];    
} tEmployee;

typedef uint_fast8_t bool_t;

/*****************************************************************************
 * flush stdin... this should be standard but somewhy you need to reinvent
 * it all the time...
 *****************************************************************************/
static inline void flush_stdin()
{
    char ch;

    do {
        ch = getchar();
    } while ((ch != '\n') && (ch != EOF));
}

/*****************************************************************************
 * reads a line of text from a stream.
 *****************************************************************************/
static inline bool_t xio_fgetline(FILE *stream, char *linebuf, size_t szline)
{
    fgets(linebuf, szline, stream);

    // find last character.
    char *lc = linebuf + strlen(linebuf) - 1;

    // the only case when lc is a null is if the program memory
    //     has been altered. In this case, it should crash anyway.
    //     therefore I skip a nullcheck before chomping.

    // chomp linebuf.
    if (*lc == '\n') {
        *lc = 0;
    }

    // string is {0} after chomping.
    if (strlen(linebuf) == 0) {
        return 0;
    }

    return 1;
}

/*****************************************************************************
 * reads a line of text from stdin.
 *****************************************************************************/
static inline bool_t xio_getline(char *linebuf, size_t szline)
{
    return (xio_fgetline(stdin, linebuf, szline));
}

int main(int argc, char **argv)
{
    int32_t n;
    tEmployee *employee = (tEmployee *)0;

    printf("How many employees would you like to register?\n");
    scanf("%i", &n);
    flush_stdin();

    employee = (tEmployee *)malloc(n * sizeof(tEmployee));

    for (int32_t i = 0; i < n; ++i) {
        printf("Name: ");
        xio_getline(employee[i].name, sizeof(employee[i].name));

        printf("Code: ");
        scanf("%i", &employee[i].code);
        flush_stdin();

        printf("Address: ");        
        xio_getline(employee[i].add, sizeof(employee[i].add));

        printf("%s\n", employee[i].name);
        printf("%i\n", employee[i].code);
        printf("%s\n", employee[i].add);
    }

    free(employee);
    return 0;
}
person Dmitry    schedule 13.05.2016