C Ошибка ввода данных программирования

Я пишу это, чтобы получить информацию о студенте (полное имя, идентификатор и средний балл за последние 3 триместра, поэтому я использовал структуры и цикл for для добавления информации, однако, после 1-го выполнения цикла for (что означает, что у студента 2 ) мой 1-й и 2-й ввод отображаются на экране вместе. Как я могу предотвратить это простым и понятным способом? (PS: я уже пытался поставить getchar(); в конце цикла for, и это сработало , однако; я не должен его использовать, потому что мы не учились в классе)

Часть программы c, в которой происходит моя ошибка:

 #include <stdio.h>

struct Student {
  char name[30];
  int id;
  float gpa[3];
};

float averageGPA ( struct Student [] );

int main()
{
  int i;
  float average;
  struct Student studentlist[10];
  i=0;

  for (i; i<10; i++)
  {
     printf("\nEnter the Student %d full name: ", i+1);
     fgets(studentlist[i].name, 30, stdin);
     printf("Enter the Student %d ID: ", i+1);
     scanf("\n %d", &studentlist[i].id);
     printf("Enter the Student %d GPA for the 1st trimester: ", i+1);
     scanf("%f", &studentlist[i].gpa[0]);
     printf("Enter the Student %d GPA for the 2nd trimester: ", i+1);
     scanf("%f", &studentlist[i].gpa[1]);
     printf("Enter the Student %d GPA for the 3rd trimester: ", i+1);
     scanf("%f", &studentlist[i].gpa[2]);
  }

  average = averageGPA(studentlist);

  printf("\n\nThe average GPA is %.2f", average); 

  return 0;
}

float averageGPA (struct Student studentlist[])
{
  int i;
  float total = 0.0, average = 0.0;
  for (i=0; i<10; i++)
  {
    total =  studentlist[i].gpa[0] + studentlist[i].gpa[1] + studentlist[i].gpa[2]; 
  }

  average = total / 30 ;
  return average;
}

Выход компьютера:

Enter the Student 1 full name: mm

Enter the Student 1 ID: 12

Enter the Student 1 GPA for the 1st trimester: 3

Enter the Student 1 GPA for the 2nd trimester: 4

Enter the Student 1 GPA for the 3rd trimester: 3

Enter the Student 2 full name: Enter the Student 2 ID: <<<<< Here is the problem!!

person MohdAziz    schedule 07.09.2012    source источник
comment
Пожалуйста, отредактируйте свой пост с правильным отступом. Кажется, что он исчез во время копирования/вставки.   -  person Lundin    schedule 07.09.2012
comment
почему вы не используете scanf для поля name в отличие от всех других ваших входных данных?   -  person Vishal    schedule 07.09.2012


Ответы (4)


Используйте scanf следующим образом, чтобы прочитать имя ученика:

scanf(" %[^\n]",studentlist[i].name);

Первый пробел в спецификаторе формата важен. Он отменяет новую строку из предыдущего ввода. Формат, кстати, предписывает читать до тех пор, пока не встретится новая строка (\n).

[Редактировать: Добавление объяснения по запросу]

Спецификатор формата для приема строки — %s. Но он позволяет вводить только непробельные символы. Альтернативный способ — указать допустимые (или недопустимые, в зависимости от сценария) символы в квадратных скобках.

В квадратных скобках вы можете указать отдельные символы, диапазоны или их комбинацию. Чтобы указать исключаемые символы, поставьте перед ними знак вставки (^).

Таким образом, %[a-z] будет означать, что любой символ между a и z (включая оба) будет принят.

В вашем случае нам нужно, чтобы каждый символ, кроме новой строки, был принят. Итак, мы придумали спецификатор %[^\n]

Вы получите больше информации об этих спецификаторах из Интернета. Вот одна ссылка для удобства: http://beej.us/guide/bgc/output/html/multipage/scanf.html

Пробел в начале фактически «поглощает» любое предшествующее пустое пространство, оставшееся от предыдущего ввода. Вы можете сослаться на ответ здесь для подробного объяснения: scanf: %[^\n] пропускает второй ввод, а %[^\n] — нет. почему?

person vaisakh    schedule 07.09.2012
comment
Я попробовал это, и это сработало отлично, но я не знаю, как вам удалось прийти к этому спецификатору формата, небольшое объяснение было бы здорово. Спасибо большое за вашу помощь - person MohdAziz; 07.09.2012
comment
В любое время .. :) Я вижу, что ответы до сих пор не выбраны. Всем было бы полезно, если бы один из ответов был выбран как правильный. если на ваш вопрос ответили, то есть.. - person vaisakh; 08.09.2012
comment
Есть ли способ выбрать конкретный ответ или просто проголосовать? - person MohdAziz; 12.09.2012
comment
Рядом с каждым ответом должна быть галочка. Нажмите на ответ, который нужно принять. Дополнительную информацию можно найти в разделе часто задаваемых вопросов.. О.. и, кстати, добро пожаловать в Stackoverflow :) - person vaisakh; 13.09.2012

Попробуйте съесть новую строку после последней scanf:

scanf("%f ", &studentlist[i].gpa[2]);
         ^

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

person cnicutar    schedule 07.09.2012
comment
Я пробовал это, но это не сработало. После того, как я подключаю последний gpa и нажимаю ввод, он продолжает давать мне пробелы (например, когда вы нажимаете ввод в msword). - person MohdAziz; 07.09.2012

Но вы должны использовать getchar(), чтобы отбросить символ новой строки, который все еще находится во входном буфере после вашего последнего scanf("%f"), который в соответствии с заданным форматом преобразует число с плавающей запятой и оставляет в буфере все остальные символы.

Если вы не можете использовать getchar(), используйте еще один fgets() в конце цикла.. но, конечно, getchar() будет лучше

Изменить для объяснения: всякий раз, когда вы вводите символы на клавиатуре, они попадают во входной буфер, ожидающий обработки вашим приложением. getchar() просто «потребляет» один символ из этого буфера (возвращая его), ожидая допустимого символа, если буфер пуст. scanf("%f") только "потребляет" символы, приводящие к плавающим числам. Итак, когда вы набираете «5.12‹enter›», scanf читает и удаляет из буфера «5.12», оставляя «‹enter›». Таким образом, следующая функция fgets() уже находит новую строку в буфере и немедленно возвращается; вот почему вы должны использовать getchar(): игнорируя возвращаемое значение, вы успешно отбрасываете «‹enter›» из буфера. Наконец, обратите внимание, что если в буфере есть только "‹enter›", scanf("%f") отбрасывает его (поскольку его нельзя преобразовать в число с плавающей запятой) и ждет другого приложения, блокирующего ввод.

И последнее замечание: входной поток буферизуется политикой вашей ОС по умолчанию, в том смысле, что приложение не получает никаких символов, пока вы не наберете «‹enter›».

person Jack    schedule 07.09.2012
comment
Scanf() с пробелом в строке формата (см. другие ответы) был бы еще лучше, поскольку он отбрасывает только символы пробела и в то же время любое количество символов пробела (например, CR + LF) и а не один. - person Lundin; 07.09.2012
comment
Дело в том, что я точно не знаю, что делает getchar(), но после серфинга в Интернете я увидел так много людей, использующих его, поэтому я попробовал, и это сработало. Не могли бы вы предоставить мне информативное объяснение функции getchar() и большое спасибо за вашу помощь. заботиться - person MohdAziz; 07.09.2012

Я бы просто отказался от scanf(). Используйте fgets() для всех полей ввода и преобразуйте их в числовые с помощью atoi() и atof().

person Scooter    schedule 07.09.2012