C input with scanf - c

In C programming language the scanf() function reads from keyboard characters or numbers?
For example, if the format is %d and I write "1" or "a", the scanf() reads only integer numbers and ignore other characters?
I have read on a book that the scanf() reads charcters from keyboard, and then converts the characters into data types specified by formats.
Can anyone explain me.
Thanks in advance.

If you look at the function signature of scanf(), it returns an int, which you can use to check whether the scan operation was successful or if it failed:
On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file.
If a reading error happens or the end-of-file is reached while reading, the proper indicator is set (feof or ferror). And, if either happens before any data could be successfully read, EOF is returned.
If an encoding error happens interpreting wide characters, the function sets errno to EILSEQ.

%c is used to read characters
%d is for integers
When you write
scanf("%d",&i)
if i is declared as integer then if you try to print the value of i then it will print integer.
And when you write
scanf("%c",&i)
if i is declared as character then if you try to print the value of i then it will print charcter.
Also check scanf:-
Format specifiers: A sequence formed by an initial percentage sign (%)
indicates a format specifier, which is used to specify the type and
format of the data to be retrieved from the stream and stored into the
locations pointed by the additional arguments.
Return Value
On success, the function returns the number of items of the argument
list successfully filled. This count can match the expected number of
items or be less (even zero) due to a matching failure, a reading
error, or the reach of the end-of-file.
If a reading error happens or the end-of-file is reached while
reading, the proper indicator is set (feof or ferror). And, if either
happens before any data could be successfully read, EOF is returned.
If an encoding error happens interpreting wide characters, the
function sets errno to EILSEQ.

Scanning stops when an input character does not match such a format character. Scanning also stops when an input conversion cannot be made
http://www.linuxmanpages.com/man3/scanf.3.php

You are wondering infrastructure details of your communication with the program in a prompt(cmd, terminal etc.) environment i guess. When you write the code like:
int x;
scanf("%d", &x);
and compile and run, program stops and waits for the scanf line to be entered anything to the underlined environment such as cmd or terminal. When you or your user start entering anything from the keyboard, program does nothing maybe buffering. But when you press enter,
program tries to get the characters(up to the whitespace) and convert them to desired variable type that is integer, because you said so in the scanf function parameter. This mentioned conversion happens implicitly. If you wrote:
char str[20];
scanf("%s", str);
and enter some keys from the keyboard, then the underlined behavior of the conversion would be different.
For summary, logic is simple:
if you define varible int, then the program tries to convert to int; if you define float, program tries to convert to float. But all entered keys come from the character sequence and this is called string. I recommend you to read the usage of scanf
and let it go.

scanf() reads charcters fromstdin, and then converts the characters into data types specified by formats. ?
Man page on scanf clearly answers this,
The scanf() family of functions scans input according to format as described
below. This format may contain conversion specifications; the results from such
conversions, if any, are stored in the locations pointed to by the pointer
arguments that follow format. Each pointer argument must be of a type that is
appropriate for the value returned by the corresponding conversion specification.

Related

Integer taken with scanf() does not get its intended value in C

Here is my C code:
#include "stdio.h"
int main()
{
int minx, x;
printf("Enter two ints: ");
scanf( "%d-%d", &minx, &x);
printf("You wrote: minx is %d x is %d", minx, x);
}
When the input is 5-3 or 5- 3 the output is You wrote: minx is 5 x is 3 which makes sense. However, when the input is 5 -3 or 5 - 3 or 6 -4 the output is You wrote: minx is 5 x is 8. I expected - to skip white spaces, so I expected minx to be 5 and x to be 3,6 and 4 for the other input. This also happens when - in %d-%d changed to ?, *, + even with the same inputs. I know it is probably because of that space after the first int. Here it says only three format specifiers do not skip white space — Whitespace before %c specification in the format specifier of scanf function in C. Did I get this wrong? Why - does not skip leading space here? What is the actual problem here and what is the cause of it? Why is it 8? What other operators or chars can lead to similar problems?
Let's look at your scanf format specifier "%d-%d" in detail:
%d skip whitespace if necessary, then read an integer
- match a literal '-' character
%d skip whitespace if necessary, then read an integer
So the inputs 5-3 and 5- 3 both work just fine. But when the input is 5 -3 (or anything else with a space before the -) the parse fails, because scanf does not immediately see the - it expects.
If that's not what you expected, or not what you want, or if that doesn't make sense, or if that's not how you'd like scanf to work, I'm afraid that's too bad: scanf works the way it works.
How can you fix this? That depends in part on why you included the - character in your format string in the first place.
You can use %d%d or %d %d, which will simply read two integers (separated by at least one whitespace character). If there's a - character preceding the second integer, that integer will be read as negative.
You can use %d -%d, which will skip (arbitrary) whitespace before it tries to match the - character.
You can use two separate scanf calls.
If you do continue to use scanf, you really need to check its return value so that your program can detect the case that the expected inputs were not matched.
Finally, you can use something other than scanf.
My recommendation to you depends on the ultimate purpose of this program.
If it's just for learning, then minimize the amount of time you spend fussing with the way your program does input at all. For example, if you need to read an integer, use one %d. As long as you can get the numbers you need into your program (so that you can test the rest of your program), you're fine. If there are things you can type that cause scanf to get confused, just don't worry about it, just don't type those things. Don't try to do anything fancy — that's not what scanf is for.
If this is a "real" program, that does have to accept arbitrary user input, or input with a specific syntax (like with that - in the right place), and if you need to deal gracefully with incorrect input, printing appropriate errors, and not reading the wrong values or getting confused — then run, do not walk, away from scanf, and never use it again. It is effectively impossible to write a program that performs high-quality input using scanf. Not even a C expert can do it. It's simply not worth it. You will spend five times as long, and get an inferior result, than if you simply abandoned scanf and read your input a line at a time using fgets or the like, then parsed the input line (perhaps even using sscanf — but again, check its return value).
Addendum: It's true, all format specifiers — with three exceptions (%c, %[…] scan sets, and %n) — skip whitespace before beginning their work. But format specifiers are things that begin with %. Literal characters in the format string must match exactly, and there's no implicit whitespace skipping for them. If you want to skip whitespace at spots in the input other than before the % format specifiers that do it, you can include a literal whitespace character (usually a single space) in your format string.
You need to check the return value of scanf. You should be doing this, in general, for every library function you call that has a return value.
In your case, scanf would return 2 (meaning two output parameters were set) for valid input. If you get any other return value, the input does not match the format you specified, and you should ignore the contents of your output parameters minx and x.

Understanding scanf behaviour

I am very new to C programming. My sir gave this code to find the maximum of n numbers. When I do as Sir says things are perfect i.e Write a number when the line - Type the number of numbers and write numbers in a row like 7 8 9 10 when Type the numbers pop up.
#include <stdio.h>
main()
{
int n, max, number, i;
printf("Type the number of numbers");
scanf("%d", &n);
if(n>0)
{
printf("Type the numbers");
scanf("%d",&number);
max=number;
for(i=1; i<n; i++)
{
scanf("%d", &number);
if(number>max)
max=number;
}
printf("MAX=%d \n", max);
}
}
But if I write suppose - 5 8 9 10 7 6 - then the program understands it like --
It puts n = 5 then puts number = 8 then loop executes number changes to 9 then number changes to 10 till 6 and then gves max.
So how is scanf working here? It takes digit individually although they are written in a row with spaces?
From the horse's mouth:
7.21.6.2 The fscanf function
...7 A directive that is a conversion specification defines a set of matching input sequences, as
described below for each specifier. A conversion specification is executed in the
following steps:
8 Input white-space characters (as specified by the isspace function) are skipped, unless
the specification includes a [, c, or n specifier.284)
9 An input item is read from the stream, unless the specification includes an n specifier. An
input item is defined as the longest sequence of input characters which does not exceed
any specified field width and which is, or is a prefix of, a matching input sequence.285)
The first character, if any, after the input item remains unread. If the length of the input
item is zero, the execution of the directive fails; this condition is a matching failure unless
end-of-file, an encoding error, or a read error prevented input from the stream, in which
case it is an input failure.
10 Except in the case of a % specifier, the input item (or, in the case of a %n directive, the
count of input characters) is converted to a type appropriate to the conversion specifier. If
the input item is not a matching sequence, the execution of the directive fails: this
condition is a matching failure. Unless assignment suppression was indicated by a *, the
result of the conversion is placed in the object pointed to by the first argument following
the format argument that has not already received a conversion result. If this object
does not have an appropriate type, or if the result of the conversion cannot be represented
in the object, the behavior is undefined.
...
12 The conversion specifiers and their meanings are:
d Matches an optionally signed decimal integer, whose format is the same as
expected for the subject sequence of the strtol function with the value 10
for
the base argument. The corresponding argument shall be a pointer to
signed integer.
...
284) These white-space characters are not counted against a specified field width.
285) fscanf pushes back at most one input character onto the input stream. Therefore, some sequences
that are acceptable to strtod, strtol, etc., are unacceptable to fscanf.
The processing for scanf is exactly the same; the only difference is that scanf always reads from standard input.
Examples:
Suppose you type SpaceSpaceSpace123Enter in response to the first prompt; the input stream then contains the sequence {' ', ' ', ' ', '1', '2', '3', '\n'}. When you call scanf( "%d", &n );, scanf reads and discards the leading blank spaces, then reads and matches the sequence {'1', '2', '3'}, converts it to the integer value 123, and assigns the result to n. Since there was a successful conversion and assignment, scanf returns 1.
If the input stream contains the sequence {' ', ' ', ' ', '1', '2', '.', '3', '\n'}, scanf reads and discards the leading blanks, then reads and matches the sequence {'1', '2'}, converts it to the integer value 12, and assigns the result to n. The input stream will still contain {'.', '3', '\n'}. Since there was a successful conversion and assignment, scanf will return 1.
If the input stream contains the sequence {'.', '3', '\n'}, then there is no matching sequence of characters ('.' is not a valid character in a decimal integer). scanf will leave the . unread and leave the value of n unchanged. Since there was not a successful conversion and assignment, scanf returns 0 to indicate a matching failure.
If an end-of-file is signaled on the input stream before any matching characters have been read, or if there's some other input error, scanf does not assign any new value to n and returns EOF to indicate an input failure.
The "%d" in scanf("%d",&number); causes has 3 stages of scanning user text input into an int.
0 or more leading whites-space, like ' ', '\n', '\t' and some others are read and discarded.
Numeric text like "123", "-123", "+123" is read until until a non-numeric character is read. (or end-of-file, or a rare input error).
That non-numeric character is put back into stdin for subsequent input calls.
If step 2 is successful in reading at least 1 digit, the function returns 1. Good code checks the returned value.
if (scanf("%d",&number) != 1) Handle_UnexpectedInput();
The important thing is that '\n' is not so special with scanf("%d",&number);. It acts like a separator like another white-space or non-numeric text.
'\n' does cause the buffered stdin to accept the line of user input for processing by the various scanf() calls.
Here it is simplified explanation (not overly simplified, I hope) on how scanf works from a user point of view:
The arguments are divided in two parts:
The first part is a “format string”. The string is made of at least
one format specifier. In its simplest form a specifier begins with
% and it is followed by a letter that specifies the type of
variable you’re expecting (“%d” – I’m expecting an integer). The
number of specifiers must match the number of parameters and types
in the second part.
The second part is made of one or more addresses to locations memory
where the data you input will be stored. The pointed types must
match the specifiers.
When called, the function will repeat the following steps, starting with the first specifier and the first pointer, until the end of format string is detected:
Read and discard any white-space until a non-white-space character is found (white-space: space, tab, NL, at least);
Read characters up to first white-space or a character that do not match the expected input for current specifier;
Convert them to the type of current specifier and
Store the result in the location pointed by the current pointer.
There are three typical beginner mistakes which will result in undefined behavior (crash, most likely):
You forget the address-of operator &.
The specifier and the type do not match.
The number of specifiers do not match the number of pointers.
int d;
scanf( "%d", d ); // no &
scanf_s( "%s", &d ); // s do not match int
scanf_s( "%d%d", &d ); // too many specifiers
when you press the keyboard, you are filling a buffer on your computer. the scanf will read the amount of consecutive data untill it hit a space, so "1234 43", on the code scanf("%d") you are saying "read one number", and 1234 is one number, that's what it will read.
But if you have a loop that will execute that scanf again, the number "43" is currently in the reading buffer, and scanf will read it without stopping.
The manual for scanf doesn't explains that and it's a bit confusing for a newcomer to understand why the application is not stopping there to read a new number.
Let me explain as simply as possible...
scanf reads bytes from the standard input stream stdin.
Let's say the input you give is "23 67 21 99\n" (The \n is from when you pressed Enter).
Then each next call to scanf will start reading from this input buffer and it will interpret what is sees as whatever you tell it ("%d", etc) while separating inputs by an empty character. This could be a new line, a space, a tab, etc.
While there are still bytes to be read, scanf will not wait for you to input. That is what is happening here.
let's keep it simple. I assume you don't know anything about buffer or stdin.
scanf is used to take input from user. Whenever you type a number and press 'space' or ' enter' on keyboard the number is entered into program for further purposes. When you type scanf("%d",&n); it mean take integer input from the user and store it on the address of variable n.

How does scanf() reads when input is not in specified format?

The input and output for the following program are given below:
#include<stdio.h>
int main(){
int a=0, b=100, c=200;
scanf("%d,%d,%d",&a,&b,&c);
printf("%d %d %d\n",a,b,c);
return 0;
}
Input-1:
1,2,3
Output-1:
1 2 3
So the first output is correct and as expected.
Input-2:
1 2 3
Output-2:
1 100 200
Here it reads first integer correctly but I am not able to understand, how the scanf() reads the data after first integer when we are not giving the input in specified format?
As values entered after first integer are not assigned to any variables, What happens to those values?
Are they written on some random memory locations?
Here it reads first integer correctly but I am not able to understand,
how the scanf() reads the data after first integer when we are not
giving the input in specified format?
scanf stops at the first mismatch and leaves the rest of the target objects untouched. You can inspect the return value to determine how many "items" scanf matched.
As values entered after first integer are not assigned to any
variables, What happens to those values?
The unmatched data is left in the input buffer, available for a subsequent read using scanf, fgets etc.
I used getchar() and putchar() after last printf() statement in the
program but nothing was read.
Strange. You should be able to get away with something like:
int ch;
while ((ch = getc(stdin)) != EOF)
putchar(ch);
TL;DR answer: It doesn't. It stops reading.
To clarify, input is not in specified format is a matching failure for scanf(). That is why it's always recommended to check the return value of scanf() to ensure all the input items got scanned successfully.
In the second input scenario, the scanf() has failed to scan all the input parameters because of the format mismatch of expected and received inputs [and you have no idea of that]. Only the value of a has been scanned successfully, and reflected.
[Just for sake of completeness of the answer]:
After the input value of a, due to the absence of a , in the input, mismatch happened and scanf() stopped scanning, returning a value of 1. That's why, b and c prints out their initial values.

prevent user to input incorrect data type

I was trying to scan only allowed types in my c program.
I have a variable which is integer.I want to scan again while user input is character.
int usercount;
do{
printf("enter user count");
scanf("%d",&usercount);
}
while(!isdigit(usercount));
But when input is char it's in a infinite loop.Can anyone help?
The scanf() function has a return value. It tells you how many "conversions" it succeeded in doing. In this case, it will return 0 if failed to interpret the input as an integer. Your code should inspect the return value.
In general, to be less sensitive with "stuff" left in the buffered input queue, you should read a full line at a time with fgets(), and then use sscanf() (or any other parsing function, like strtoul()) to parse the data.

How to prevent menus from glitching when scanf expects an int but receives characters (C)

Take for instance:
printf("Continue?\n>>");
scanf("%d", &cont);
getchar();
Normally I add the getchar() to prevent the program from infinite looping (reading off the '\n' character from the buffer). However, when used with a menu following this statement the extra characters are read in and any scanfs following the character input (up to the number of characters input) are skipped.
What I want to figure out is how to prevent it from skipping forward through several sections of my program when it reads in a type of input other than an int. Would this be best solved by putting it inside of a loop that won't continue until the variable is in the expected domain?
Consider using fgets and sscanf instead. Load a line's worth of input, then parse only that line instead of the entire stdin.
Check the value returned by scanf. The return value indicates the number variables that were assigned to. If you're expecting an int and the user types in a character, scanf should return zero.
Try including a "%*s" input specifier. See http://www.cplusplus.com/reference/clibrary/cstdio/scanf/
Instead of reading an integer, just read a string and convert it to a number (atoi). Two problems that may occur:
Char buffer not big enough. To prevent this you can read char by char from the input and realloc the buffer if necessary.
String is not a number. Atoi will return some default value (0 or -1, don't remember). You just have to check the string somehow.

Resources