Проверка символов при вводе в C

void main(){
    int a;
    scanf("%d",&a); // Need to check there is no character entered
    printf("%d",a);
}

Здесь, если я передам abc, он напечатает 0, если я передам 123abc, он напечатает 123, но мне нужно выдать ошибку в обоих условиях.

Вот как проверить, вводятся ли в качестве ввода только числа, и выдать сообщение об ошибке, если в качестве ввода вводится символ. Можно ли проверить сохранение int в качестве типа входных данных или мне следует использовать массив символов и проверить условие isalpha, пройдя массив.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
83
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вам нужно проверить значение, возвращаемое scanf. (Это всегда верно)

#include <stdio.h>
#include <ctype.h>

int
main(void)
{
    int a;
    char b;
    if ( (2 == scanf("%d%c", &a, &b)) && isspace(b) ){
        printf("%d\n", a);
    } else {
        fputs("Invalid input!\n", stderr);
    }
}

Вы всегда должны проверять значение, возвращаемое scanf. В вашем случае (с использованием %d), если первый символ во входном потоке не является цифрой, scanf вернет 0. Если ввод 123x, scanf прочитает 123 и вернет 1. Если ввод представляет собой строку, которая слишком долго представляться int, поведение не определено.

Используя %4d%c, вы можете проверить значение во входном потоке после того, как целое число будет прочитано из потока. В нашем случае мы считаем это ошибкой, если это значение не является пробелом. Это будет работать довольно хорошо для интерактивного использования, в котором вы ожидаете, что значение будет новой строкой, но это не работает, если поток заканчивается допустимым целым числом. В этом случае scanf вернет 1, и вам нужно будет добавить больше логики, чтобы решить, произошла ли ошибка. Решение здесь такое же, как и у всех проблем, связанных с scanf: перестаньте использовать scanf, потому что это ужасный инструмент.

Также обратите внимание, что я использую %4d в строке формата, а не %d. Вы можете увеличить это число, но у вас должен быть модификатор ширины, чтобы предотвратить неопределенное поведение на входах, которые не могут быть представлены целым числом. Но чтобы узнать модификатор максимальной ширины, который вы можете использовать, вам нужно знать INT_MAX на машине, а фактический диапазон значений, которые можно прочитать, не является полным диапазоном int. Язык только гарантирует, что INT_MAX не меньше 32767, поэтому %5d небезопасно, так как ввод 99999 приведет к UB. Решение здесь такое же, как и решение всех проблем, связанных с scanf: перестаньте использовать scanf, потому что это ужасный инструмент.

Ключ к вашей проблеме: проверьте значение, возвращаемое scanf, чтобы узнать, сколько конверсий он сделал. Но пока подчеркну в третий раз: прекратите использовать scanf, это ужасный инструмент!

Почему ограничение %4d цифр?

Weather Vane 01.11.2022 17:31

@WeatherVane отличный вопрос. Я отредактировал решение, чтобы дать небольшой ответ.

William Pursell 01.11.2022 17:33

В защиту scanf, он вообще для этого не предназначен.

Neil 01.11.2022 18:42
Ответ принят как подходящий

Код, который снова запрашивает ввод, если он неверен:

#include <stdio.h>

int main(void)
{
    int number;
    printf("Your input: ");
    while(scanf("%d", &number)!=1 || getchar()!='\n')
    {
        scanf("%*[^\n]%*c");
        printf("you must enter an integer: ");
    }

    printf("%d\n", number);

    return 0;
}

Первый scanf делает запись, второй очищает буфер клавиатуры. getchar проверяет, является ли следующий символ клавишей ввода.

Поздравляю, лучший способ сделать scanf пригодным для использования, который я когда-либо видел. +1

Jabberwocky 01.11.2022 18:30

Все еще переходит в бесконечный цикл, когда вы закрываете stdin. ./a.out < echo lol.

Neil 01.11.2022 18:45

Спецификатор формата %d сообщает scanf, что нужно прочитать десятичное целое число со знаком. Тип данных int представляет отрицательные целые числа, ноль и положительные целые числа. Любой из следующих шаблонов представляет допустимое целое число:

  • Отрицательный знак, за которым следуют числовые цифры. Пример -3
  • Положительные целые числа могут начинаться со знака плюс и сопровождаться числовыми цифрами. Пример: +73
  • Числовые цифры, которым не предшествует знак плюс или минус, будут считываться как положительные целые значения. Пример: 9

scanf ищет эти типы шаблонов при преобразовании ввода в указанный тип. Как показывают приведенные выше ответы, функция scanf возвращает количество элементов в списке аргументов, которые были успешно прочитаны.

#include <stdio.h>


int main(void)
{
   int returnValue;
   int number;
   
   /* 
   scanf("%d", &number) will return a value of 1 if it successfully reads an 
   integer value. Otherwise it will return a value of 0 if it fails to read an 
   integer value.

   The specifier d says to extract any number of decimal digits (0-9),
   optionally preceded by a sign (+ or -). 
   
   Suppose the user enters the following input: -3 followed by the enter key press.
   
   Think of scanf as reading one input character at a time. The first character read
   is the minus sign '-'. Since the number may start with a minus, scanf reads the
   next character '3'. This is a decimal digit which fits the integer pattern. 
   The next character is the enter key press '\n' which causes scanf to stop reading
   the input. The characters '-' '3' are converted to the integer value -3 and 
   are stored in the variable named number.

   The scanf function successfully read one integer value, so it returns a value
   of 1 and stores it in the variable returnValue.

   
   Suppose the user enters the following input: -3.0
   The value of -3 is read and stored in the variable number. When scanf reads 
   the character '.', it stops reading information from the input stream 
   because this character is not a decimal digit. The value -3 is stored in
   the variable number. The scanf function returns a value of 1 because it 
   successfully read an integer.

   The characters '.' '0' '\n' still remain in the standard input (stdin) buffer,
   waiting to be read from the input stream. The next time scanf is called 
   to read from the stream, it will try to read the '.' If the program needs 
   to read another number, we write code to clear the input stream of all 
   remaining characters so that scanf does not fail when next trying to read 
   numeric input.

   
   Suppose the user enters the following input: a-1

   The character 'a' is not a numeric digit. It will not be read from the 
   input stream. scanf will return a value of zero and will not write 
   write any value into the memory of the variable number.

   The characters 'a' '-' '1' \n' all remain in the input stream.

   scanf will skip over leading whitespace characters when trying to read an integer.
 */
   
   returnValue = scanf("%d", &number);

   printf("number: %d, returnValue: %d\n", number, returnValue);
   
   return 0;
}

Другие вопросы по теме