Не знаю, как правильно использовать функцию getchar / C - PullRequest
0 голосов
/ 03 ноября 2018

Я хотел создать функцию, которая вводит символ в консоль, но проблема в том, что иногда, когда я ввожу 'a', он считается пустым символом, и программа просит меня повторно ввести символ. Это функция:

char readChar()
  {
    char character;
    character = getchar();
    character = toupper(character);
    while(getchar() != '\n' && getchar() != '\0');
    return character;
  }

Ответы [ 3 ]

0 голосов
/ 03 ноября 2018

Преобразование множества комментариев в ответ.

1 . Обратите внимание, что getchar() возвращает int, а не char. И ваш цикл должен учитывать EOF больше, чем нулевой байт, хотя, вероятно, нормально определять нулевые байты.

Мое лучшее предположение о проблеме заключается в том, что иногда вы делаете scanf("%d", &i) или что-то подобное, а затем вызываете эту функцию - но scanf() не читает новую строку , поэтому ваша функция читает новую строку оставленные предыдущими операциями ввода / вывода. Но без MCVE ( Минимальный, Полный и Проверяемый пример ) мы не сможем доказать, что моя гипотеза верна.

2 . Кроме того, ваш цикл «оставшаяся часть строки» должен вызывать getchar() только один раз на каждую итерацию; Вы называете это дважды. Один из вариантов будет использовать:

int readChar(void)
{
    int c;
    while ((c = getchar()) != EOF && isspace(c))
        ;
    if (c == EOF)
        return EOF;
    int junk;
    while ((junk = getchar()) != '\n' && junk != EOF /* && junk != '\0' */)
        ;
    return toupper(c);
}

Это пожирает пробел, пока не получит символ, не являющийся пробелом, а затем прочитает все ненужные символы до следующей новой строки. Это исправило бы мой гипотетический сценарий. Остерегайтесь EOF - всегда учитывайте EOF.

Основываясь на чтении вопросов и ответов о том, что scanf() не читает новую строку, Voltini предложил исправление:

char readChar()
{
    char character;
    scanf(" %c", &character);
    getchar(); //I just added this line
    character = toupper(character);
    return character;
}

3 . Это часто хороший способ работы. Обратите внимание, что он до сих пор не имеет отношения к EOF - вам всегда нужно беспокоиться о EOF. getchar() после scanf() будет читать новую строку, если пользователь набрал a и новую строку , но не если они набрали a-z и затем newline . Вы должны решить, что вы хотите сделать с этим - и цикл одушевления персонажа часто является хорошей идеей вместо одного getchar() вызова:

int c;
while ((c = getchar()) != EOF && c != '\n')
    ;

И в ответ на комментарий следующего содержания:

Пожалуйста, объясните важность обработки EOF.

4 . Если вы не спросите, вы не обязательно узнаете об этом! Вход и выход (I / O) и особенно вход, чреват. Пользователи не печатают то, что вы им сказали печатать; они добавляют пробелы до или после того, что вы им сказали набирать; вы ожидаете что-то короткое, например good, а они набирают supercalifragilisticexpialidocious. Иногда что-то идет не так, и больше нет данных для чтения - состояние, известное как EOF или «конец файла».

5 . В функции с char character; scanf(" %c", &character); и без проверки, если нет ввода (пользователь вводит ^ D в Unix или ^ Z в Windows, или файл данных завершился), вы не представляете, какое значение будет в переменной - он квазислучайный (неопределенный), и его использование вызывает неопределенное поведение . Это плохо. Кроме того, в коде вопроса у вас есть этот цикл, который никогда не закончится, если пользователь укажет EOF.

while (getchar() != '\n' && getchar() != '\0')    // Should handle EOF!
    ;

6 . И, чтобы добавить к сложности, если обычный символ представляет собой тип без знака, назначение символа и тестирование на EOF всегда будет неудачным, и если обычный символ является типом со знаком, вы обнаружите EOF на действительном символе (часто often - маленький латинский буква y с диарезом в кодировках Unicode и 8859-1 или 8859-15). Вот почему мой код использует int c; для ввода символов. Итак, как вы можете видеть (я надеюсь), есть веские причины, по которым вы должны всегда обращать внимание на EOF. Это может произойти, когда вы этого не ожидаете, но из-за этого ваш код не должен зацикливаться.

Я не уверен, как и где ... реализовать это ... в моем коде.

7 . Есть две части к этому. Один из них находится в функции readChar(), которая должна возвращать int, а не char (по тем же причинам, по которым getchar() возвращает int, а не char), или которая нуждается в альтернативе интерфейс, такой как:

bool readChar(char *cp)
{
    int c;
    while ((c = getchar()) != EOF && isspace(c))
        ;
    if (c == EOF)
        return false;
    *cp = toupper(c);
    while ((c = getchar()) != '\n' && c != EOF /* && c != '\0' */)
        ;
    return true;
}

так что вы можете позвонить:

if (readChar(&character))
{
    …process valid input…
}
else
{
    …EOF or other major problems — abandon hope all ye who enter here…
}

С помощью функции, корректно определяющей EOF, вы можете исправить свой вызывающий код, чтобы он обрабатывал EOF (индикацию ошибки) с readChar().

Обратите внимание, что empТела петли обозначены точкой с запятой с отступом на отдельной строке. Именно так K & R (Керниган и Ритчи в языке программирования C - 1988) писал циклы с пустыми телами, поэтому вы нашли его широко используемым.

Со временем вы обнаружите, что огромное количество кода, написанного вами на C, предназначено для обработки ошибок.

0 голосов
/ 03 ноября 2018

Не читайте потенциально дважды за цикл, как с while(getchar() != '\n' && getchar() != '\0'); @ Джонатаном Леффлером


Чтобы прочитать один и первый символ из строки пользовательского ввода:

Текстовый поток - это упорядоченная последовательность символов, состоящая из строк , каждая строка состоящий из нуля или более символов плюс завершающий символ новой строки. Будь то последняя строка требует, чтобы завершающий символ новой строки определялся реализацией. C11dr §7.21.2 2

  1. Использовать getchar(). Возвращает int в диапазоне unsigned char или EOF.

  2. Прочитать остаток строки .

Пример кода, немного похожий на OP.

int readChar_and_make_uppercase() {
  int ch = getchar();
  if (ch != '\n' && ch != EOF) {
    // read rest of line and throw it away
    int dummy;
    while ((dummy = getchar()) != '\n' && dummy != EOF) {
      ;
    }
  }
  return toupper(ch);
}
0 голосов
/ 03 ноября 2018

Видимо, я забыл позаботиться о символе перевода строки, хранящемся в буфере (поправьте меня, если я ошибаюсь).

char readChar()
{
    char character;
    scanf(" %c", &character);
    getchar(); //I just added this line
    character = toupper(character);
    return character;
}
...