2010-03-02 6 views
1

Я пишу программу, которая работает с файлами. Мне нужно иметь возможность вводить данные в виде структур и, в конечном счете, читать их. Проблема у меня есть на данный момент с этим кодом:fgets from stdin problems [C]

typedef struct { 
    char* name; 
    ..... 
}employeeRecord; 
employeeRecord record; 

char name[50]; 

if(choice == 1) 
    { 
     /*Name*/ 
     printf("\nEnter the name:"); 
     fgets(name,50,stdin); 
     record.nameLength = strlen(name) -1; 
     record.name = malloc(sizeof(char)*record.nameLength); 
     strcpy(record.name,name); 
     /*Other data, similar format...*/ 

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

+1

Вы должны обновить вопрос с кодом, который на самом деле показывает проблема (кстати, это должно быть 'strlen (name) + 1', а не' - 1'). – caf

+1

Я не вижу здесь никакого вопроса, каковы конкретные проблемы, которые у вас есть? – nos

ответ

2

Я пробовал ваш код и не могу воспроизвести проблему. Следующий код работает так, как вы ожидали, он запрашивает имя, дожидается, когда вы наберете имя, затем предложите адрес и т. Д.

Мне интересно, не нужно ли читать stdin и очистите его, прежде чем запрашивать больше ввода?

typedef struct { 
    char* name; 
    char* address; 
}employeeRecord; 

int readrecord(employeeRecord &record) 
{ 
    char name[50]; 
    char address[100]; 

    printf("\nenter the name:"); 
    fgets(name, sizeof(name), stdin); 
    record.nameLength = strlen(name) + 1; 
    record.name = malloc(sizeof(char)*record.nameLength); 
    strcpy(record.name,name); 

    printf("\nenter the address:"); 
    fgets(address, sizeof(address), stdin); 

    ...  
} 

Incidently, вы хотите добавить 1 к STRLEN (имени), не вычитают 1. или, если вы хотите, имя хранятся в вашей записи без завершающего нуля, то вам нужно использовать тетср, чтобы скопировать строку в ваша запись, а не strcpy.

Edit:

Я вижу от комментариев, которые вы используете scanf прочитать значение выбора, это оставляет \ п во входном буфере, который затем подхвачен первым fgets вызовом. Вместо этого вы должны использовать fgets для чтения в строке выбора, а затем sscanf для анализа значения вне ввода. например

int choice; 
char temp[50]; 
fgets(temp, sizeof(temp), stdin); 
sscanf(temp, "%d", &choice); 

, который должен полностью решить проблему промывки stdin.

+2

Должен быть комментарий zillion о StackOverflow уже о том, как fflush (stdin) не поддерживается ничем, и если он работает, это происходит только случайно. –

+0

@ Zan Lynx: Кстати, это не случайно fflush (stdin) работает на Windows, они сделали это нарочно. Это только * nix-платформы, которые считают, что fflush undefined для stdin - хорошая идея. –

+0

@ Джона Кноэля: спасибо за кучу вашего редактирования, это именно то, что я искал. И теперь он отлично работает, очень ценится. – Blackbinary

3

Новая строка все еще находится в stdin из предыдущего вызова функции, которая не читала новую строку с ввода. Очистите stdin, прочитав, пока вы не зачитали новую строку - не путем промывки stdin, как предложили другие.

EDIT: Спасибо, Алок, за исправление!

+1

Как говорит Бернард, нужно будьте осторожны, чтобы никогда не оставлять пропущенные пробелы непрочитанными (например, scanf), если предназначен ориентированный на линию ввод. Чтение всего строки за раз (например, через fgets), а затем использование sscanf в строке, прочитанной в одной подходящей опции. – Tronic

+2

Если 'fgets' читает всю строку, символ новой строки * not * в буфере stdin. Скорее всего, OP читает 'choice' одним из' getchar() ',' getc() ',' fgetc() 'или эквивалентным. Если 'fgets' не читал всю строку, входной буфер должен быть больше. (Даже цитата в вашем ответе гласит, что символ новой строки читается.) –

+0

Алок: ты прав, мозг. : o – Bernard

2

Вы, вероятно, использовали scanf, чтобы прочитать choice, прежде чем звонить fgets, чтобы прочитать название. scanf, возможно, оставил новую строку в stdin, которую ваш код ошибочно вводит для ввода пустого имени. Если это действительно так, постарайтесь не использовать scanf (используйте fgets для извлечения choice и используйте atoi для преобразования в int или strcmp для сравнения «1 \ n» и т. Д.). Код должен иначе работать с указанными ниже модификациями, чтобы учесть тот факт, что fgets также читает символ новой строки в буфер (который вы, вероятно, хотите, раздели):

#define MY_LENOF(x) (sizeof(x)/sizeof((x)[0])) 

    char choice[3] = { 0 }; /* example of how to initialize to all NULs */ 
    if (!fgets(choice, MY_LENOF(choice), stdin)) { 
    fprintf(stderr, "Premature end of input\n"); 
    exit(1); 
    } 

    if (strcmp(choice, "1\n") == 0) { 
    /*Name*/ 
    printf("\nEnter the name:"); 
    if (!fgets(name, MY_LENOF(name), stdin)) { 
     /* if fgets fails it leaves name unchanged, so we reset it to "" */ 
     name[0] = '\0'; 
    } 
    /* good practice to use srtnlen in order not to overrun fixed buffer */ 
    /* not necessarily a problem with fgets which guarantees the trailing NUL */ 
    size_t nameLength = strnlen(name, MY_LENOF(name)); 
    assert(name[nameLength] == '\0'); 
    if (nameLength - 1 > 0 && name[nameLength - 1] == '\n') { 
     /* strip trailing newline */ 
     name[--nameLength] = '\0'; 
    } else if (nameLength >= MY_LENOF(name) - 1) { 
     fprintf(stderr, "Name is too long\n"); 
     exit(1); 
    } else { 
     fprintf(stderr, "Premature end of input\n"); 
     exit(1); 
    } 

    record.nameLength = nameLength; 
    record.name = malloc(sizeof(char)*(record.nameLength + 1)); 
    strcpy(record.name, name); 
+0

Наверное, вы не хотите использовать 'atoi', так как он не выполняет проверку ошибок (' sscanf' и проверка возвращаемого значения должно работать, но мой C ржавый). Хороший ответ. – Bernard

+0

правильно. поэтому, в его случае (я полагаю, он не выполняет арифметику, просто проверяет), он должен использовать 'strcmp' – vladr

+0

@Bernard:' sscanf' довольно груб для проверки ошибок, если вы не будете осторожны (например, '"% d " 'будет соответствовать' "123foo" '). Проверка неправильного ввода с помощью 'strtod' и т. П. Намного проще. – jamesdlin