Проверить scanf на ошибку

Say If i want an input to be [Name] [Name] How would I detect [Name] [Name] [Name] and return error? Here is what I have so far, char in[20]; char out[20]; scanf(" %s %s", out, in);

This is homework, so you might be required to work under certain (arbitrary) restrictions. However, the phrase «scanf error handling» is something of an oxymoron in the C programming language.

The best way to do this is to read in a line/other suitable chunk and parse it with C string functions. You can do it in one line of scanf but there are many drawbacks:

  1. You can guard against buffer overflows, but if a buffer isn’t large enough you can’t recover.
  2. You can specify specific character ranges for your strings, but it starts to look a little regexy, and the behavior of the "%[" format of scanf isn’t mandated in the standard.
  3. You can check for a third name, but the code looks unintuitive — it doesn’t look like you only want two names, it looks like you want three. scanf also gives you very little control over how you handle whitespace.

EDIT: I initially thought from your question that the names were contained in brackets (a la "[Bruce] [Wayne]") but it now appears that was merely your convention for denoting a placeholder.

Anyway, despite my intense dislike of scanf, it has its uses. The biggest killer (for me) is the inability to distinguish between line endings and simple space separation. To fix that, you can call fgets to read the data into a buffer, then call sscanf on the buffer. This gives you both a) safer reading (scanf messes with the ability of other more straightforward functions to read from a buffer) and b) the benefits of scanf formats.

If you have to use scanf, your format basically be this:

" %s %s %s"

With the third being undesirable. As @Constantinius’s answer shows, you’d need to read data into three buffers, and check whether or not the third passed. However, if you’re reading multiple consecutive lines of this data, then the first entry of the next line would satisfy the third slot, falsely giving you an error. I highly recommend using fgets and sscanf or ditching the sscanf for more precise manual parsing in this case.

Here’s a link to the fgets man page if you missed the one I snuck in earlier. If you decide to ditch sscanf, here are some other functions to look into: strchr (or strspn, or strcspn) to find how long the name is, strcpy or memcpy (but please not strncpy, it’s not what you think it is) to copy data into the buffers.

I have a program which accepts an integer from the user and uses this number in an addition operation.

The code which I am using to accept the number is this:

scanf("%d", &num);

How can I validate the input such that if the user enters a letter or a number with the decimal point, an error message is displayed on the screen?

alk's user avatar

alk

69.3k10 gold badges100 silver badges250 bronze badges

asked Mar 5, 2013 at 15:58

Matthew's user avatar

1

You should use scanf return value. From man scanf:

Return Value

These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

So it may look like this:

if (scanf("%d", &num) != 1)
{
    /* Display error message. */
}

Notice it doesn’t work for «numbers with the decimal point». For this you should rather use parsing and strtol for instance. It may be a bit more complex.

Community's user avatar

answered Mar 5, 2013 at 16:00

md5's user avatar

md5md5

23.3k3 gold badges44 silver badges93 bronze badges

3

Read your input as text using either scanf with a %s conversion specifier or by using fgets, then use the strtol library function to do the conversion:

#define MAX_DIGITS 20 // maximum number of decimal digits in a 64-bit integer

int val;
int okay = 0;

do
{
  char input[MAX_DIGITS+2]; // +1 for sign, +1 for 0 terminator
  printf("Gimme a number: ");
  fflush(stdout);
  if (fgets(input, sizeof input, stdin))
  {
    char *chk = NULL; // points to the first character *not* converted by strtol
    val = (int) strtol(input, &chk, 10);
    if (isspace(*chk) || *chk == 0)
    {
      // input was a valid integer string, we're done
      okay = 1;
    }
    else
    {
      printf(""%s" is not a valid integer string, try again.n", input);
    }
  }
} while (!okay);

answered Mar 5, 2013 at 16:50

John Bode's user avatar

John BodeJohn Bode

117k18 gold badges116 silver badges194 bronze badges

I have a program which accepts an integer from the user and uses this number in an addition operation.

The code which I am using to accept the number is this:

scanf("%d", &num);

How can I validate the input such that if the user enters a letter or a number with the decimal point, an error message is displayed on the screen?

alk's user avatar

alk

69.3k10 gold badges100 silver badges250 bronze badges

asked Mar 5, 2013 at 15:58

Matthew's user avatar

1

You should use scanf return value. From man scanf:

Return Value

These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

So it may look like this:

if (scanf("%d", &num) != 1)
{
    /* Display error message. */
}

Notice it doesn’t work for «numbers with the decimal point». For this you should rather use parsing and strtol for instance. It may be a bit more complex.

Community's user avatar

answered Mar 5, 2013 at 16:00

md5's user avatar

md5md5

23.3k3 gold badges44 silver badges93 bronze badges

3

Read your input as text using either scanf with a %s conversion specifier or by using fgets, then use the strtol library function to do the conversion:

#define MAX_DIGITS 20 // maximum number of decimal digits in a 64-bit integer

int val;
int okay = 0;

do
{
  char input[MAX_DIGITS+2]; // +1 for sign, +1 for 0 terminator
  printf("Gimme a number: ");
  fflush(stdout);
  if (fgets(input, sizeof input, stdin))
  {
    char *chk = NULL; // points to the first character *not* converted by strtol
    val = (int) strtol(input, &chk, 10);
    if (isspace(*chk) || *chk == 0)
    {
      // input was a valid integer string, we're done
      okay = 1;
    }
    else
    {
      printf(""%s" is not a valid integer string, try again.n", input);
    }
  }
} while (!okay);

answered Mar 5, 2013 at 16:50

John Bode's user avatar

John BodeJohn Bode

117k18 gold badges116 silver badges194 bronze badges

Other Alias

fscanf, sscanf, vscanf, vsscanf, vfscanf

ОБЗОР

#include <stdio.h>
int scanf(const char *format, …);
int fscanf(FILE *stream, const char *format, …);
int sscanf(const char *str, const char *format, …);


#include <stdarg.h>
int vscanf(const char *format, va_list ap);
int vsscanf(const char *str, const char *format, va_list ap);
int vfscanf(FILE *stream, const char *format, va_list ap);

Требования макроса тестирования свойств для glibc
(см. feature_test_macros(7)):

vscanf(), vsscanf(), vfscanf():

_XOPEN_SOURCE >= 600 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;

или cc -std=c99

ОПИСАНИЕ

Группа функций scanf() считывает вводимую информацию в соответствии с
форматом format так, как описано ниже. В формате могут указываться
определители преобразования (conversion specifications); результаты
каждого преобразования, если они производились, сохраняются по адресам
параметров указателей, передаваемых после format. Каждый параметр
указатель должен быть того же типа, что и значение, получаемое в
результате преобразования данных в соответствии с форматом.

Если количество определителей преобразования в format превышает
количество параметров указателей, то результат не определён. Если
количество параметров указателей превышает количество определителей
преобразования, то лишние параметры указатели вычисляются, но
игнорируются.

Функция scanf() считывает информацию из стандартного потока ввода
stdin; fscanf() считывает информацию из потока, на который указывает
stream, а sscanf() считывает информацию из символьной строки, на
которую указывает str.

Функция vfscanf() является аналогом vfprintf(3) и читает информацию из
потока, на который указывает указатель stream, используя список
указателей переменной длины (смотрите stdarg(3)). Функция vscanf()
считывает список параметров переменной длины из стандартного ввода, а
функция vsscanf() считывает его из строки. Эти функции являются аналогами
функций vprintf(3) и vsprintf(3), соответственно.

Строка format состоит из последовательности инструкций (directives),
которые описывают порядок обработки входных символов. Если обработка
инструкции завершается с ошибкой, то чтение прекращается и scanf()
завершает работу. «Отказом» может быть: ошибка ввода, то есть
недоступность входных символов или ошибка совпадения, то есть получены
неподходящие данные (смотрите далее).

Инструкцией может быть:

  • Последовательность пробельных символов (пробел, табуляция, символ новой
    строки и т. д.; смотрите isspace(3)). Эта инструкция совпадает с любым
    количеством пустого места, включая отсутствие данных.
  • Обычный символ (т. е., отличный от пробельного или «%»). Такой символ должен
    точно совпадать со следующим символом входных данных.
  • Определитель преобразования, который начинается с символа «%»
    (процент). Последовательность символов ввода преобразуется в соответствии с
    определителем, а результат помещается в соответствующий параметр
    указатель. Если следующий элемент ввода не соответствует определителю
    преобразования, то преобразование завершается с ошибкой — ошибкой
    совпадения
    .

Каждый определитель преобразования в format начинается с символа «%»
или последовательности символов «%n$» (смотрите о разнице далее) за
которым следует:

  • Необязательный символ подавления назначения «*»: scanf() читает данные
    как предписано определителем преобразования, но отбрасывает
    их. Соответствующий параметр указатель необязателен, и этот определитель
    не учитывается в счётчике успешных назначений, возвращаемом scanf().
  • Необязательный символ «m». Используется в строковых преобразованиях (%s,
    %c, %[) и освобождает вызывающего от необходимости выделять
    соответствующий буфер для хранения входных данных: вместо этого scanf()
    выделяет буфер достаточного размера и присваивает адрес этого буфера
    соответствующему параметру указателю, который должен быть указателем на
    переменную char * (эту переменную не нужно инициализировать перед
    вызовом). Вызывающий должен вызвать free(3) для этого буфера, как только
    он станет ненужным.
  • Необязательное целое десятичное число, которое задаёт максимальную ширину
    поля
    . Чтение символов прерывается по достижении этого максимума или при
    нахождении несовпадающего символа, неважно что случится раньше. В
    большинстве преобразований начальные пробельные символы отбрасываются
    (исключения приведены далее), и эти отброшенные символы не учитываются в
    максимальной ширине поля. В преобразованных строках ввода сохраняется
    конечный байт null (») для отметки конца ввода; в максимальной
    ширине поля он также не учитывается.
  • Необязательный символ модификатора типа. Например, модификатор типа l
    используется в преобразованиях целых чисел, например с помощью %d, для
    указания того, что соответствующий параметр указатель ссылается на
    long int, а не на int.
  • Определитель преобразования, который задаёт тип входного преобразования.

Определители преобразования в format бывают двух видов: начинающиеся с
«%» и начинающиеся с «%n$». Эти два вида не должны использоваться
одновременно в строке format, за исключением случая, когда строка,
содержащая определители «%n$», может включать %% и %*. Если в
format содержатся определители «%», то они задаются в порядке появления
параметров указателей, указанных после. В форме «%n$» (есть в
POSIX.1-2001, но отсутствует в C99), n — это десятичное целое, которое
задаёт в какое место должен быть помещён ввод, то есть указывает на
расположение n-го параметра указателя, передаваемого после format.

Преобразования

Следующие символы модификаторов типа (type modifier characters) могут
появляться в определении преобразования:

h
Обозначает, что преобразование будет одним из d, i, o, u, x,
X или n и следующий указатель является указателем на short int или
на unsigned short int (но не int).
hh
Как h, но следующий указатель — указатель на signed char или
unsigned char.
j
Как h, но следующий указатель — указатель на intmax_t или
uintmax_t. Этот модификатор появился в C99.
l
Обозначает, что преобразование будет одним из d, i, o, u, x,
X или n и следующий указатель является указателем на long int или
unsigned long int (но не int), или что преобразование будет одним из
e, f или g и следующий указатель является указателем на double
(но не float). Указание двух символов l эквивалентно L. Если
используется с %c или %s, то соответствующий параметр считается
указателем на широкий символ или строку широких символов, соответственно.
L
Обозначает, что преобразование будет одним из e, f или g и
следующий указатель является указателем на long double или преобразование
будет одним из d, i, o, u или x и следующий указатель
является указателем на long long.
q
Эквивалентен L. Данный определитель отсутствует в ANSI C.
t
Как h, но следующий указатель — указатель на ptrdiff_t. Этот
модификатор появился в C99.
z
Как h, но следующий указатель — указатель на size_t. Этот модификатор
появился в C99.

Доступны следующие определители преобразования:

%
Совпадает с литерой «%». То есть %% в строке формата соответствует
одиночному символу данных «%». Преобразование не выполняется (но начальные
пробельные символы отбрасываются) и назначения не происходит.
d
Совпадает с необязательным знаковым десятичным целым; следующий указатель
должен быть указателем на int.
D
Эквивалентно ld; оставлено только для обратной совместимости (замечание:
есть только в libc4. В libc5 и glibc %D просто игнорируется, что приводит
к непонятным ошибкам в старых программах).
i
Совпадает с необязательным знаковым целым; следующий указатель должен быть
указателем на int. Целое считывается как шестнадцатеричное число, если
начинается с 0x или 0X, как восьмеричное, если начинается с 0 и как
десятичное в остальных случаях. Используются только символы, подходящие для
работы с выбранным основанием системы счисления.
o
Совпадает с необязательным беззнаковым восьмеричным целым; следующий
указатель должен быть указателем на unsigned int.
u
Совпадает с необязательным беззнаковым десятичным целым; следующий указатель
должен быть указателем на unsigned int.
x
Совпадает с необязательным беззнаковым шестнадцатеричным целым; следующий
указатель должен быть указателем на unsigned int.
X
Эквивалентно x.
f
Совпадает с необязательным знаковым числом с плавающей запятой; следующий
указатель должен быть указателем на float.
e
Эквивалентно f.
g
Эквивалентно f.
E
Эквивалентно f.
a
(C99) Эквивалентно f.
s
Совпадает с последовательностью непробельных символов; следующий указатель
должен указывать на первый элемент массива символов достаточной длины для
сохранения входной последовательности и завершающего байта null
(»), который добавляется автоматически. Входная строка обрывается
при появлении пробельного символа или достижении максимальной ширины поля,
неважно что случится раньше.
c
Совпадает с последовательностью символов, чья длина задаётся максимальной
шириной поля
(по умолчанию 1); следующий указатель должен быть указателем
на char, и должно быть достаточно места для всех символов (завершающий
байт null не добавляется). Обычный пропуск начальных пробелов не
выполняется. Чтобы пропустить пробелы, явно укажите их в формате.
[
Совпадает с непустой последовательностью символов из задаваемого набора
допустимых символов; следующий указатель должен быть указателем на char и
должно быть достаточно места для всех символов в строке плюс завершающий
байт null. Обычный пропуск начальных пробелов не выполняется. Строка будет
состоять (или нет) из символов определённого набора; набор задаётся
указанием символов между символом открывающей скобки [ и закрывающей
скобки ]. Набором определяются исключающиеся символы, если первым
символом после открывающей скобки является символ диакритического знака
(^). Чтобы включить закрывающую скобку в набор, укажите её первым
символом после открывающей скобки или диакритического знака; в любой другой
позиции она закрывает набор. Символ переноса также является
специализированным; если он указывается между двумя символами, то в набор
добавляются все лежащие в промежутке символы. Чтобы добавить в набор символ
переноса укажите его последним, перед конечной закрывающей
скобкой. Например, [^]0-9-] означает, что «все символы, кроме закрывающей
скобки, цифр от 0 до 9 и переноса». Строка обрывается при появлении символа
не из набора (или, при указании символа диакритического знака, из) или при
достижении ширины поля.
p
Совпадает со значением указателя (как выводится при %p в printf(3));
следующий указатель должен быть указателем на void.
n
Ничего не ожидается; вместо этого количество символов, использованных к
настоящему времени из ввода, сохраняется по следующему указателю, который
должен быть указателем на int. Это не преобразование и не
увеличивает счётчик, возвращаемый функцией. Назначение может подавляться при
указании символа подавления назначения *, но влияние этого на
возвращаемое значение не определено. Поэтому преобразования %*n лучше не
использовать.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

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

Если конец входных данных был достигнут раньше, чем произошло хотя бы одно
совпадение или при ошибке совпадения возвращается значение EOF. Значение
EOF также возвращается при ошибке чтения; в этом случае для потока
устанавливается индикатор ошибки (смотрите ferror(3)), а в errno
указывается номер ошибки.

ОШИБКИ

EAGAIN
Файловый дескриптор stream помечен как неблокирующий, а чтение вызвало бы
блокировку.
EBADF
Неправильный файловый дескриптор для stream или он не открыт на чтение.
EILSEQ
Из входной байтовой последовательности невозможно создать корректный символ.
EINTR
Операция чтения была прервана сигналом; смотрите signal(7).
EINVAL
Недостаточно параметров или format равен NULL.
ENOMEM
Не хватает памяти.
ERANGE
Результат преобразования целого превысил бы размер, который можно хранить в
соответствующем целочисленном типе.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes(7).

Интерфейс Атрибут Значение
scanf(),
fscanf(),

sscanf(),
vscanf(),

vsscanf(),
vfscanf()

безвредность в нитях безвредно (MT-Safe locale)

СООТВЕТСТВИЕ СТАНДАРТАМ

Функции fscanf(), scanf() и sscanf() соответствуют C89, C99 и
POSIX.1-2001. В этих стандартах не определена ошибка ERANGE.

Определитель q в 4.4BSD используется для long long, а определители
ll или L используются в GNU для преобразования целых чисел.

Версия Linux этих функций основана на библиотеке GNU libio. Более
точное описание функций можно найти в документации в формате info на
GNU libc (glibc-1.08).

ЗАМЕЧАНИЯ

Модификатор выделения-назначения «a»

Первоначально, в библиотеке GNU C поддерживалось динамическое выделение
памяти для входных строк при указании символа a (нестандартное
расширение) (это свойство существует до glibc 2.0). То есть можно указать
scanf() выделить буфер под входную строку, передав в указателе только
указатель на буфер *buf:


    char *buf;

    scanf(«%as», &buf);

Использование буквы a для этой цели проблематично, так как a также
используется в стандарте ISO C как синоним f (ввод данных с плавающей
запятой). В POSIX.1-2008 для назначения с выделением определён модификатор
m (смотрите в ОПИСАНИЕ выше).

Заметим, что модификатор a недоступен, если программа скомпилирована
посредством gcc -std=c99 или gcc -D_ISOC99_SOURCE (если не определён
_GNU_SOURCE); в этом случае a рассматривается как определитель чисел с
плавающей запятой (смотрите выше).

Поддержка модификатора m была добавлена в glibc начиная с версии 2.7, и в
новых программах нужно использовать этот модификатор вместо a.

Стандартизированный в POSIX модификатор m имеет дополнительные
преимущества над a:

*
Он может также применяться к определителям преобразования %c (например,
%3mc).
*
Исчезает неоднозначность с определителем преобразования чисел с плавающей
запятой %a (не подвержен влиянию gcc -std=c99).

ДЕФЕКТЫ

Все функции полностью соответствуют C89, но предоставляют дополнительные
определители q и a, а также дополнительные возможности определителей
L и l. Последнее может считаться дефектом, так как это изменяет
поведение определителей, заданное в C89.

Некоторые комбинации модификаторов типов и определителей преобразования,
определённые в ANSI C, не имеют смысла (например, %Ld). Хотя они могут
иметь хорошо описанное поведение в Linux, это не обязательно так на других
архитектурах. Поэтому, обычно, лучше использовать модификаторы, не
определённые в ANSI C, то есть использовать q вместо L в комбинации с
преобразованием d, i, o, u, x и X или ll.

Работа q отличается от работы в 4.4BSD, так как может использоваться при
преобразовании вещественных числе подобно L.

ПРИМЕР

Чтобы использовать определитель динамического выделения при преобразовании,
укажите m в качестве модификатора длины (в виде %ms или
%m[диапазон]). Вызывающий должен вызвать free(3) для
возвращённой строки как в следующем примере:

char *p;
int n;
errno = 0;
n = scanf("%m[a-z]", &p);
if (n == 1) {
    printf("чтение: %sn", p);
    free(p);
} else if (errno != 0) {
    perror("scanf");
} else {
    fprintf(stderr, "Нет совпадающих символовn");
}

Как показано в примере выше, необходимо вызывать free(3) только, если при
вызове scanf() была прочитана строка.

Понравилась статья? Поделить с друзьями:
  • Проверить сочинения на орфографические ошибки
  • Проверить flac файл на ошибки
  • Проверить систему стабилизации рено каптур ошибка
  • Проверив расчеты нами была найдена ошибка исправьте ошибки
  • Проверить систему снижения токсичности рено каптур как исправить ошибку