Запрос пользователя на C

Я новичок в программировании на C, и я нахожу подсказку для ввода пользователем довольно сложной задачи для начинающих, таких как я.

Я просто пишу простой C-код, чтобы постоянно запрашивать ввод пользователя, пока пользователь не введет отрицательное число, которое останавливает программу. В некотором смысле, я хочу, чтобы формат запроса ввода пользователя был похож на способ, которым мы занимались в python, где:

Enter a number: (userinput a number here and press enter) 

и выход будет:

 The number you entered is 10 (for example) 

который еще не остановил цикл и запрашивает другое число, так как не вводится отрицательное число.

 Enter a number: (userinput another number which is negative) 

Выход:

 The number you entered is -10 (for example) 

и программа останавливается.

Я пробовал писать в C:

 #include  int value = 0; while (value >= 0) { do { printf("Enter a number: "); scanf("%d",&value); printf("The number you entered is %d", &value); } 

но я не могу заставить оператор «Ввести число:» отображать сначала, когда я запускаю программу, так как она сразу запрашивает ввод пользователя в начале без сообщения с запросом, пока я не введу целое число, появится подсказковое сообщение что является противоположностью того, что я хотел. Поблагодарите за помощь.

Одна из самых больших проблем, с которыми сталкиваются новые программисты на С, – правильно обрабатывать пользовательский ввод, особенно при использовании семейства функций scanf . Зачем? При использовании scanf вы должны учитывать все символы, оставшиеся во входном буфере ( stdin здесь), в случае соответствующего сбоя . Зачем? Когда происходит соответствующий сбой , scanf останавливает обработку символов в точке сбоя, а символ (символы), вызвавший отказ, остается в вашем входном буфере непрочитанным , просто ожидая, чтобы укусить вас на следующем попытке ввода.

Дальнейшее усложнение проблемы заключается в том, как спецификаторы преобразования разницы scanf обрабатывают пробельные символы. Ваши спецификаторы числовых входов и %s будут потреблять ведущие пробелы, в то время как остальные спецификаторы преобразования этого не делают. Это означает, что если вы вводите ввод с помощью другого, чем числовой или %s спецификатор преобразования, вы должны учитывать и удалять завершающий '\n' прежде чем пытаться преобразовать следующий символ или символьный class .

Тем не менее, всякий раз, когда вы используете scanf , вам необходимо проверить возврат, чтобы вы могли определить, отменил ли пользователь вход с созданным вручную EOF , произошел ли соответствующий или входной сбой.

На минимальном уровне вы должны проверить возврат и обработать любое возrotation, указывающее на меньшее количество ожидаемых конверсий. В вашем случае, при одном преобразовании в int , вы должны проверить, что возврат равен 1 прежде чем использовать value . В противном случае вы можете легко вызвать Undefined Behavior . Теперь просто проверка того, произошли ли все преобразования, не позволяет вам различать EOF или соответствующий сбой – оставляя вас без информации, необходимой для продолжения, сделайте все возможное, чтобы выйти из недействительного ввода, например

 #include  int main (void) { int value = 0; while (value >= 0) { printf("Enter a number: "); if (scanf("%d",&value) != 1) { fputs ("error: invalid input\n", stderr); return 1; } printf("The number you entered is %d\n", value); } return 0; } 

Пример использования / вывода

 $ ./bin/scanfpos2 Enter a number: 1 The number you entered is 1 Enter a number: 10 The number you entered is 10 Enter a number: foo error: invalid input 

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

Правильный способ обработки ввода с помощью scanf состоит в том, чтобы покрыть все возможные условия ошибки и изящно реагировать на соответствующий сбой, очистив буфер ввода символов-нарушителей, позволяя продолжить ввод. Это помогает иметь небольшую вспомогательную функцию для очистки stdin а не для повторного включения цикла очистки каждый раз, когда scanf используется в вашем коде. Короткий пример:

 /** remove all characters that remain in stdin */ void empty_stdin (void) { int c = getchar(); while (c != '\n' && c != EOF) c = getchar(); } 

Который просто читает все символы из stdin пока не встретится '\n' или EOF . Объединение этого с расширенной проверкой возврата scanf позволит вам обработать соответствующий отказ изящно, например

 #include  /** remove all characters that remain in stdin */ void empty_stdin (void) { int c = getchar(); while (c != '\n' && c != EOF) c = getchar(); } int main (void) { int value = 0; while (value >= 0) { int rtn; /* variable to store return on scanf */ printf ("Enter a number: "); rtn = scanf ("%d",&value); if (rtn == EOF) { /* handle EOF */ fputs ("user canceled input.\n", stderr); break; } else if (rtn == 0) { /* handle matching/input failure */ fputs ("error: invalid input\n", stderr); empty_stdin(); } else /* good input - output value */ printf("The number you entered is %d\n", value); } return 0; } 

Пример использования / вывода

 $ ./bin/scanfpos2 Enter a number: 1 The number you entered is 1 Enter a number: 10 The number you entered is 10 Enter a number: foo error: invalid input Enter a number: -1 The number you entered is -1 

Здесь, если вводится не целое число, например foo , оно захватывается, символы удаляются из stdin а петлевые запросы снова вводятся для ввода.

Посмотрите все. C не является питоном. Python скрывает большую часть деталей реализации от вас, чтобы существенно защитить вас от экземпляров так же, как это, но также имеет свои недостатки. С C ничего не скрыто от вас. Вам дается свободное владение для записи в память, которой у вас нет, многократно пытайтесь прочитать форму входного буфера после отказа преобразования и т. Д. Вы несете ответственность за детали.

Наконец, все это основная причина, fgets с вводом с помощью fgets или POSIX getline , рекомендуется для новых пользователей. С буфером достаточно большого размера (не экономьте размер), fgets будет считывать строку за раз из входного буфера, предотвращая оскорбление оставшихся символов, просто ожидая, что вас снова укусят. getline будет выделять буфер достаточного размера независимо от того, сколько времени занимает линия, но вы несете ответственность за освобождение памяти, когда вы закончите с ней. Исследуйте как альтернативы использованию scanf . Вы всегда можете вызвать sscanf в буфере, удерживая строку после того, как она будет прочитана, чтобы проанализировать числовые значения из нее.