Scanning with %c or %s - c

I had to do a program for college in which I should separate, between a certain amount of people, those who liked and the ones who disliked something.
so I did this:
char like[100];
printf("Like? Y or N \n");
scanf ("%c", like);
The program compiled, but didn't work the way it should. The user was not able to write "y or n" when asked "Like?"
So I tried this:
char like[100];
printf("Like? Y or N \n");
scanf ("%s", like);
And it worked. But I don't know why it worked. Can somebody please explain me the difference between %c and %s in a scanf?

First, please do some basic research before coming here - questions like this can usually be answered with a quick Google search or checking your handy C reference manual.
char inputChar; // stores a single character
char inputString[100] = {0}; // stores a string up to 99 characters long
scanf( " %c", &inputChar ); // read the next non-whitespace character into inputChar
// ^ Note & operator in expression
scanf( "%s", inputString ); // read the next *sequence* of non-whitespace characters into inputString
// ^ Note no & operator in expression
You would use %c when you want to read a single character from the input stream and store it to a char object. The %c conversion specifier will not skip over any leading whitespace, so if you want to read the next non-whitespace character, you need a blank before the %c specifier in your format string, as shown above.
You would use %s when you want to read a sequence of non-whitespace characters from the input stream and store them to an array of char. Your target array must be large enough to store the input string plus a terminating 0-valued character. The %s conversion specifier skips over any leading whitespace and stops reading at the first whitespace character following the non-whitespace characters.
Both %c and %s expect their corresponding argument to have type char * (pointer to char); however, in the first case, it's assumed that the pointer points to a single object, whereas in the second case, it's assumed that the pointer points to the first element of an array. For inputChar, we must use the unary & operator to obtain the pointer value. For inputString, we don't, because under most circumstances an expression of type "array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
Your code works fine as it is, but it's a bit confusing to read a single character and store it to an array.
Using %s without an explicit field width is risky; if someone types in more than 100 non-whitespace characters, scanf will happily store those extra characters to memory following inputString, potentially clobbering something important. It's generally safer to write something like
scanf( "%99s", inputString ); // reads no more than 99 characters into inputString
or to use fgets() to read input strings instead:
fgets( inputString, sizeof inputString, stdin );
Please check §7.21.6.2 of the online draft of the C language standard for a complete description of all of the conversion specifiers for the *scanf functions.

%s is used for string of characters and reads subsequent characters until it finds a whitespace(blank, newline or tab). Whereas %c is used for single character and reads the next character. If there are more than 1 character, including any whitespace character, it is read and stored in next iteration.

Related

Why is this creating two inputs instead of one

https://i.imgur.com/FLxF9sP.png
As shown in the link above I have to input '<' twice instead of once, why is that? Also it seems that the first input is ignored but the second '<' is the one the program recognizes.
The same thing occurs even without a loop too.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
int randomGen, upper, lower, end, newRandomGen;
char answer;
upper = 100;
lower = 1;
end = 1;
do {
srand(time(0));
randomGen = rand()%(upper + lower);
printf("%d\n", randomGen);
scanf("%s\n", &answer);
}while(answer != '=');
}
Whitespace in scanf format strings, like the \n in "%c\n", tries to match any amount of whitespace, and scanf doesn’t know that there’s no whitespace left to skip until it encounters something that isn’t whitespace (like the second character you type) or the end of input. You provide it with =\n, which fills in the %c and waits until the whitespace is over. Then you provide it with another = and scanf returns. The second time around, the character could be anything and it’d still work.
Skip leading whitespace instead (and use the correct specifier for one character, %c, as has been mentioned):
scanf(" %c", &answer);
Also, it’s good practice to make sure you actually succeeded in reading something, especially when failing to read something means leaving it uninitialized and trying to read it later (another example of undefined behaviour). So check scanf’s return value, which should match the number of conversion specifiers you provided:
if (scanf(" %c", &answer) != 1) {
return EXIT_FAILURE;
}
As has been commented, you should not use the scanf format %s if you want to read a single character. Indeed, you should never use the scanf format %s for any purpose, because it will read an arbitrary number of characters into the buffer you supply, so you have no way to ensure that your buffer is large enough. So you should always supply a maximum character count. For example, %1s will read only one character. But note: that will still not work with a char variable, since it reads a string and in C, strings are arrays of char terminated with a NUL. (NUL is the character whose value is 0, also sometimes spelled \0. You could just write it as 0, but don't confuse that with the character '0' (whose value is 48, in most modern systems).
So a string containing a single character actually occupies two bytes: the character itself, and a NUL.
If you just want to read a single character, you could use the format %c. %c has a few differences from %s, and you need to be aware of all of them:
The default maximum length read by %s is "unlimited". The default for %c is 1, so %c is identical to %1c.
%s will put a NUL at the end of the characters read (which you need to leave space for), so the result is a C string. %c does not add the NUL, so you only need to leave enough space for the characters themselves.
%s skips whitespace before storing any characters. %c does not ignore whitespace. Note: a newline character (at the end of each line) is considered whitespace.
So, based on the first two rules, you could use either of the following:
char theShortString[2];
scanf("%1s", theShortString);
char theChar = theShortString[0];
or
char theChar;
scanf("%c", &theChar);
Now, when you used
scanf("%s", &theChar);
you will cause scanf to write a NUL (that is, a zero) in the byte following theChar, which quite possibly is part of a different variable. That's really bad. Don't do that. Ever. Even if you get away with it today, it will get you into serious trouble some time soon.
But that's not the problem here. The problem here is with what comes after the %s format code.
Let's take a minute (ok, maybe half an hour) to read the documentation of scanf, by typing man scanf. What we'll see, quite near the beginning, is: (emphasis added)
A directive is one of the following:
A sequence of white-space characters (space, tab, newline, etc.; see isspace(3)). This directive matches any amount of white space, including none, in the input.
So when you use "%s\n", scanf will do the following:
skip over any white-space characters in the input buffer.
read the following word up to but not including the next white-space character, and store it in the corresponding argument, followed by a NUL.
skip over any white-space following the word which it just read.
It does the last step because \n — a newline — is itself white-space, as noted in the quote from the manpage.
Now, what you actually typed was < followed by a newline, so the word read at step 2 will be just he character <. The newline you typed afterwards is white-space, so it will be ignored by step 3. But that doesn't satisfy step 3, because scanf (as documented) will ignore "any amount of white space". It doesn't know that there isn't more white space coming. You might, for example, be intending to type a blank line (that is, just a newline), in which case scanf must skip over that newline as well. So scanf keeps on reading.
Since the input buffer is now empty, the I/O library must now read the next line, which it does. And now you type another < followed by a newline. Clearly, the < is not white-space, so scanf leaves it in the input buffer and returns, knowing that it has done its duty.
Your program then checks the word read by scanf and realises that it is not an =. So it loops again, and the scanf executes again. Now there is already data in the input buffer (the second < which you typed), so scanf can immediately store that word. But it will again try to skip "any amount of white space" afterwards, which by the same logic as above will cause it to read a third line of input, which it leaves in the input buffer.
The end result is that you always need to type the next line before the previous line is passed back to your program. Obviously that's not what you want.
So what's the solution? Simple. Don't put a \n at the end of your format string.
Of course, you do want to skip that newline character. But you don't need to skip it until the next call to scanf. If you used a %1s format code, scanf would automatically skip white-space before returning input, but as we've seen above, %c is far simpler if you only want to read a single character. Since %c does not skip white-space before returning input, you need to insert an explicit directive to do so: a white-space character. It's usual to use an actual space rather than a newline for this purpose, so we would normally write this loop as:
char answer;
srand(time(0)); /* Only call srand once, at the beginning of the program */
do {
randomGen = rand()%(upper + lower); /* This is not right */
printf("%d\n", randomGen);
scanf(" %c", &answer);
} while (answer != '=');
scanf("%s\n", &answer);
Here you used the %s flag in the format string, which tells scanf to read as many characters as possible into a pre-allocated array of chars, then a null terminator to make it a C-string.
However, answer is a single char. Just writing the terminator is enough to go out of bounds, causing undefined behaviour and strange mishaps.
Instead, you should have used %c. This reads a single character into a char.

Why does printf prints only first character of a character array whose input was read through scanf

This code is supposed to read input string through scanf and store the input string in an array declared here as msg[] and then print those input string, but when I run the code all it prints is just the first character of the string. It looks really simple but I am not able to solve it.
If I type i do, it only reports i.
#include <stdio.h>
int main()
{
char msg[5];
printf("enter the message: ");
scanf("%s",msg);
printf("entered msg is: %s", msg);
}
The answer is hidden in the comments. The OP states if i give input "i do" it prints only 'i' .
scanf only reads until the first whitespace (which is the i in i do).
If you input 4 characters, 4 characters are printed.
Please edit your question to include this information for future searchers.
The %s conversion specifier tells scanf to skip over any leading whitespace, then read up to (but not including) the next whitespace character. So when you enter "i do", the %s conversion specifier only reads "i".
If you want to read strings with whitespace, you'll have to use a different method. You can use the %[ conversion specifier like so:
scanf( "%[^\n]", msg );
which will read everything (including leading whitespace characters) until it sees the '\n' newline character and stores it to msg. Unfortunately, this (and the %s specifier above) leave you open to buffer overflow - if your user enters a string longer than msg is sized to hold, scanf will happily write those extra characters to the memory following the msg buffer, leading to all kinds of mayhem. To prevent that, you'd use a field width in the conversion spec, like so:
scanf( "%4[^\n]", msg );
This tells scanf to read no more than 4 characters into msg. Unfortunately, that width has to be hardcoded - you can't pass it as an argument the way you can with printf.
IMO, a better way to go would be to use fgets:
fgets( msg, sizeof msg, stdin );

Reading user input and stop on empty string

I want to read some user input and do something with each input. I do that using this code:
char c;
while(1){
printf("input");
c = scanf ( "%s", &c ) ;
}
This works fine . But I need it to accept even an empty input. But it just continues to next line and expects an not-empty input. How could I do that?
Current situation:
input:asdf
input:b
input:c
input:d
input:e
input:
fjhkjh
Expected :
input:asdf
input:b
input:c
input:d
input:e
input:blabla
input:f
input:
input:
Just how the cmd console works like...
UPDATE: I don't read only one single charcater, that was an example
First of all You shouldn't use %s to store input in a single character as %s is for string of characters terminated by null character(\0).
Don't use & with %s in scanf().
To store a single character make use of:
scanf("%c",&c);
or
c = getchar();
It doesn't internally work like that, you have declared it as single char but you have used %s which is a char array. the char you get as input are stored as null values.. every character you gave a b c d e are taken as null..
if you have doubt, add a printf("%s",c); below the scanf();
First of all, in your code,
scanf ( "%s", &c ) ;
invokes undefined behavior.
, as you cannot fit an incoming string inside a char.
That said, %s skips the leading white-spaces and reads the non-white-spaces up to the immediate white-space and ignores the last white-space, so you cannot read only white-spaces using %s.
Quoting C11, chapter §7.21.6.2, fscanf(), (emphasis mine)
s Matches a sequence of non-white-space characters.
If no l length modifier is present, the corresponding argument shall be a
pointer to the initial element of a character array large enough to accept the
sequence and a terminating null character, which will be added automatically.
So, we can clearly see, c is no sufficient to hold even a single char input, either, it will be out of bound access to store the terminating null.
You seem to be in need to getchar() or similar.
First you must have to use getchar(); or %c in scanf for a single character
then you can use a if for test which is the input character
char c;
while(1)
{
c=getchar();
if(c==' ')
break();
}
This is only for single character input.

Reading text with sscanf and fgets

So my text file looks similar to this
1. First 1.1
2. Second 2.2
Essentially an integer, string and then a float.
Using sscanf() and fgets() in theory, I should be able to scan this in (I have to do it in this format) but only get the integer can someone help point what I am doing wrong?
while(!feof(foo))
{
fgets(name, sizeof(name) - 1, foo);
sscanf(name,"%d%c%f", &intarray[i], &chararray[i], &floatarray[i]);
i++;
}
Where intarray, chararray, and floatarray are 1D arrays and i is an int initialized to 0.
The structure of the loop is wrong; you should not use feof() like that and you must always check the status of both fgets() and sscanf(). This code avoids overflowing the input arrays, too.
enum { MAX_ENTRIES = 10 };
int i;
int intarray[MAX_ENTRIES];
float floatarray[MAX_ENTRIES];
char chararray[MAX_ENTRIES][50];
for (i = 0; i < MAX_ENTRIES && fgets(name, sizeof(name), foo) != 0; i++)
{
if (sscanf(name,"%d. %49s %f", &intarray[i], chararray[i], &floatarray[i]) != 3)
...process format error...
}
Note the major changes:
The dot after the integer must be scanned by the format string.
The chararray has to be a 2D array to make any sense. If you read a single character with %c, it would contain the space after the first number, and the subsequent conversion specification (for the float value) would fail because the string name is not a floating point value.
The & in front of chararray[i] is not wanted when it is a 2D array. It would be needed if you were really reading a single character in a 1D array of characters instead of the whole string such as 'First' or 'Second' from the sample data.
The test checks that three values were converted successfully. Any smaller value indicates problems. With sscanf(), you'd only get EOF returned if there was nothing in the string for the first conversion specification to work on (empty string, all white space); you'd get 0 returned if the first non-blank was alphabetic or a punctuation character other than + or -, etc.
If you really want a single character instead of the name, then you'll have to arrange to read the extra characters in the word, maybe using:
if (sscanf(name,"%d %c%*s %f", &intarray[i], chararray[i], &floatarray[i]) != 3)
There's a space before the %c which is crucial; it will skip white space in the input, and then the %c will pick up the first non-blank character. The %*s will read more characters, skipping any white space (there won't be any) and then scanning a string of characters up to the next white space. The * suppresses an assignment; the scanned data won't be stored anywhere.
One of the major advantages of the fgets() plus sscanf() paradigm is that when you report the format error, you can report to the user the complete line of input that caused problems. If you use raw fscanf() or scanf(), you can only report on the first character that caused trouble, typically up to the end of the line, and then only if you write code to read that data. It is fiddlier (so the reporting is usually not very careful), and the available information is not as helpful to the user on those rare occasions when the reporting tries to be careful.
You need to change your format string to:
"%d %s %f"
The spaces are because you have spaces in your input data, the %s because you want to read a multi-character string at that point (%c only reads one character); don't worry though, as %s won't read past a space. You'll need to make sure you've got enough space in the target buffer to read the string, of course.
If you only want the first character of the second word, try:
"%d %c%s %f"
And add an extra (dummy) buffer to receive the string parsed by %s which you want to discard.
won't it be %s for string else it will only read a character with %c and then the float value might be affected.
try "%d %s %f"
%s won't help since it may read the float value itself. as far as I know, %c reads a single character. then it searches for a space that leads to problem. To scan the word, you can use a loop (terminated by a space ofcourse).

How use the FORMAT part of the sscanf function (or how to use the sscanf function in general)

I have encountered the following line in a program. From reading the manual, I know that sscanf copies from wherever argv[2] is pointed to, but I'm not sure why the FORMAT had been specified as %d and, at the same time, %c (I've seen other examples where there are more format specifiers included in the double quotations ). Is it because sscanf writes a decimal to the struct element "%g.number", a character to "%c"? Thanks!
sscanf(argv[2], " %d %c", &g.number, &c)
Yes. Reading from argv[2], one integer to g.number and one char to c.
For example argv[2] could be "123 a"
What it is specifying is zero or more white space characters followed by a number followed by zero or more white space characters, then a character. It puts the values of the number in g.number and the character in c. I'm not sure why it scans for a number then a character, but if the input does not start with a number, then nothing will be assigned to either variables and the return value of sscanf will be zero, which is how many variables it scanned in. If it fails then the value in g.number will be whatever is in the memory slot it occupies which could be anything, hence undefined.

Resources