Использование getchar() на c получает «Enter» после ввода

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

Как я могу это исправить?


person SnapDragon    schedule 19.10.2010    source источник


Ответы (5)


getchar() возвращает первый символ во входной буфер и удаляет его из входного буфера. Но другие символы все еще находятся во входном буфере (в вашем примере \n). Вам нужно очистить входной буфер перед повторным вызовом getchar():

void clearInputBuffer() // works only if the input buffer is not empty
{
    do 
    {
        c = getchar();
    } while (c != '\n' && c != EOF);
}
person KeatsPeeks    schedule 19.10.2010

Самый простой способ — отфильтровать клавишу ввода как возвращаемое значение из getchar.

char c = (char)getchar();
if ( c != '\n' ) {
  ...
}
person JaredPar    schedule 19.10.2010
comment
Я думаю, вы имели в виду getchar ()? если да, то getchar() возвращает int. - person Nyan; 19.10.2010
comment
@Nyan, getchar действительно возвращает int, но разрешено присваивать char через преобразования. Ссылка cplusplus.com/reference/clibrary/cstdio/getchar - person JaredPar; 19.10.2010
comment
@Jared: да, но char ограничен (обычно) 256 значениями, и вам нужно (обычно) 257 значений для идентификации ВСЕХ символов И EOF. Вот почему getchar() возвращает int - person pmg; 19.10.2010
comment
@pmg, я знаю об этом. Я просто демонстрирую, о чем просил OP (как проверить новую строку). - person JaredPar; 19.10.2010
comment
Хорошо, но вы можете продемонстрировать и с int c :-) - person pmg; 19.10.2010
comment
Я объявил его как int, как указывает linux man для возвращаемого значения getchar(). - person SnapDragon; 20.10.2010

Добавьте getchar() после getchar() :P

person Prasoon Saurav    schedule 19.10.2010
comment
Думаю, это сработает, но это тихая анестезия. - person SnapDragon; 20.10.2010
comment
В некоторых случаях это вызовет проблемы, когда пользователю придется дважды нажимать клавишу возврата. - person Kaitain; 05.05.2017

Как насчет

#include <stdio.h>

/*! getline() reads one line from standard input and copies it to line array
 * (but no more than max chars).
 * It does not place the terminating \n in line array.
 * Returns line length, or 0 for empty line, or EOF for end-of-file.
 */
int getline(char line[], int max)
{
  int nch = 0;
  int c;
  max = max - 1;            /* leave room for '\0' */

  while ((c = getchar()) != EOF) {
    if (c == '\n')
      break;

    if (nch < max) {
      line[nch] = c;
      nch = nch + 1;
    }
  }

  if (c == EOF && nch == 0)
    return EOF;

  line[nch] = '\0';
  return nch;
}

Источник

person Ohad Schneider    schedule 19.10.2010
comment
Я не должен использовать массивы строк или символов (это работа для курса) - person SnapDragon; 20.10.2010

Вы как бы ответили на свой вопрос; вам нужно как-то справиться с символом новой строки.

Есть несколько вариантов. Если пункты меню пронумерованы, вы можете использовать scanf() для считывания целочисленного значения и переключения на основе этого:

printf("Pick an option: ");
fflush(stdout);
scanf("%d", &option);
switch(option)
{
  case 0 : do_something(); break;
  case 1 : do_something_else(); break;
  ...
  default: bad_option(); break;
}

Преимущество этой опции заключается в том, что спецификатор преобразования %d пропускает все начальные пробелы, включая символы новой строки, поэтому вам не нужно беспокоиться о том, что какой-либо непрочитанный \n засорит входной поток (фактически, большинство спецификаторов преобразования пропускают начальные пробелы). ; %c нет, что делает его поведение очень похожим на getchar()).

Недостаток этого варианта в том, что если кто-то введет нецифровой символ во входные данные, он не будет прочитан со спецификатором преобразования %d и останется во входном потоке до вызова getchar() или scanf() со спецификатором преобразования. %s или %c спецификатор преобразования.

Лучшим вариантом является чтение всех входных данных в виде строк символов с использованием fgets(), а затем их синтаксический анализ и проверка при необходимости.

/**
 * Prints a prompt to stdout and reads an input response, writing
 * the input value to option.  
 *
 * @param prompt [in]  - prompt written to stdout
 * @param option [out] - option entered by user
 *
 * @return - 1 on success, 0 on failure.  If return value is 0, then option
 * is not changed.
 */
int getOption(const char *prompt, char *option)
{
  char input[3]; // option char + newline + 0 terminator
  int result = 0;

  printf("%s: ", prompt);  
  fflush(stdout);

  if (fgets(input, sizeof input, stdin))
  {
    /**
     * Search for a newline character in the input buffer; if it's not
     * present, then the user entered more characters than the input buffer 
     * can store.  Reject the input, and continue to read from stdin until
     * we see a newline character; that way we don't leave junk in the
     * input stream to mess up a future read.
     */
    char *newline = strchr(input, '\n');
    if (!newline)
    {
      printf("Input string is too long and will be rejected\n");
      /**
       * Continue reading from stdin until we find the newline
       * character
       */
      while (!newline && fgets(input, sizeof input, stdin))
        newline = strchr(input, '\n');
    }
    else
    {
      *option = input[0];
      result = 1;
    }
  }
  else
    printf("Received error or EOF on read\n");

  return result;
}

Да, это много работы, чтобы прочитать в одном глупом пункте меню, и это простая версия. Добро пожаловать в удивительный мир интерактивной обработки ввода на C.

person John Bode    schedule 19.10.2010