scanf doesn't assign value to a second variable - c

I've been trying to read some input from the user in C, but it doesn't seem to work as I wish it would have. I try to read a string until the user writes a colon, and then assign the colon (':') into a different variable. It works perfectly unless the colon is the first character. In that case, it simply assigns a value of -52 to both variables and reads the colon in the next scanf. I try to make it so the colon will be assigned to the second variable, even when it's first. I've tried looking online but couldn't find any solution... Any tip will be very appriciated, thanks!
My attempt:
char ch, name[200];
scanf("%[^:]%c", &name, &ch);
printf("%s\n%c\n", name, ch);
for a valid input such as:
Nadav Freedman: rest_of_input
it works and assigns "Nadav Freedman" to name and ':' to ch
but for an invalid input such as:
: rest_of_input
it simply doesn't assign any value to the variables, although I would like to save the colon to ch.

scanf stops reading as soon as any of the specifier provided doesn't match the input, so in your case if the %[^:] fails the function stops reading and the %c specifier isn't processed.
So to force it to read the colon use 2 scanfs instead of 1:
char ch, name[200];
if(scanf(" %199[^:]", name) != 1) name[0] = '\0';
scanf("%c", &ch);
printf("%s\n%c\n", name, ch);
OBS: I've also added the leading space to eliminate leading blank characters. And limited it to read up to 199 characters, so as not to exceed the size of the array name. And also changed it to update the array name to store an empty string if the first scanf fails to read anything.

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.

How to take character input in an array in C?

char name[2];
scanf("%c",name);
printf("%c",name);
I am just starting to learn C. I'm curious about the above code, what I got from the printf output, is not the same with the character I typed in. Rather the output was some funny looking symbol. Can someone explain this to me?
For the %c specifier, scanf needs the address of the location into which the character is to be stored, but printf needs the value of the character, not its address. In C, an array decays into a pointer to the first element of the array when referenced. So, the scanf is being passed the address of the first element of the name array, which is where the character will be stored; however, the printf is also being passed the address, which is wrong. The printf should be like this:
printf("%c", name[0]);
Note that the scanf argument is technically ok, it is a little weird to be passing an array, when a pointer to a single character would suffice. It would be better to declare a single character and pass its address explicitly:
char c;
scanf("%c", &c);
printf("%c", c);
On the other hand, if you were trying to read a string instead of a single character, then you should be using %s instead of %c.
Either Read a single char
char name[2];
scanf("%c",name);
printf("%c",name[0]);
Or read a string
char name[2];
scanf("%1s",name);
printf("%s",name);
You need %s since because name contains 2 elements. %c is used for single character so if you want the user to input something for e.g. "as"(without "") and the program to print it out you need %s.
char name[2];
scanf(" %s", name);
printf("%s",name);
if you give your input which contains characters less than or equal to two you will get a correct output just as your input if your input contains characters greater than 3 then it doesn't work

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.

C string using pointer

So, I want to get 5 string inputs from the user using array of pointers to strings. However, I want those strings to have white spaces and hence, I am using the %[^\n]s which accepts the string till a line break occurs. But, this code doesn't work :/ Can someone please explain why??
P.S-> If you use %s in scanf, the code works. But I want white spaces compulsorily. So, please suggest something.
Thanks in advance!
#include<stdio.h>
#include<malloc.h>
void main()
{
char *f[5];
int i;
printf("\nEnter 5 strings:\n");
for(i=0;i<5;i++)
f[i]=malloc(20);
for(i=0;i<5;i++)
scanf("%[^\n]s",f[i]);
printf("The 5 strings are:\n");
for(i=0;i<5;i++)
{
printf("\n%s",f[i]);
}
}
One major problem is the trailing 's' in your format. If you read e.g. this reference you will see that the "%[" format is terminated by the ']' character. When you have that extra 's' in the format, scanf will wait until it matches a literal 's' in the input.
And to skip the ending newline, you can add a leading whitespace in the format and scanf will automatically read and skip all whitespace (including newline) that you don't want in the next iteration of the loop.
You also should limit the length of the input scanf reads, or you might have scanf read more than you have allocated. The limit should be 19 characters (which with the string terminator is 20, like you have allocated).
So the correct format should be " %19[^\n]".
As a side-note, since you allocate all strings equally, why not simply have an array of arrays? Like
char f[5][20];
And if you still decide to go with the pointers, don't forget to free them when you're done. You don't really need it in such a simple case such as your program, but it's better to get the good habits early.
You just need to 'consume' the '\n' character after you've read what you want. Use the following format directive:
"%[^\n]%*c"
Which will read everything up to the newline into the string you pass in, then will consume a single character (the newline) without assigning it to anything (that '*' is 'assignment suppression').

Character variable string size in C

I'm trying to make a simple C program where printf asks your name and then enter it with scanf but the output only gives me one letter in the name. This is the code I used:
#include <stdio.h>
main()
{
char cName;
cName = '\0';
printf("What is your name?: ");
scanf("%c", &cName);
printf("Good evening %c", cName);
}
Output:
What is your name?: Michael
Good evening M
I tried putting [] and [20] beside cName when I declared the variable but that didn't work either. Anyone know what I'm doing wrong?
char cName[100];
printf("What is your name?: ");
scanf("%s", cName);
printf("Good evening %s", cName);
Nah... explanation, this is what I forgot about:
char c; is (most probably) 1-byte-long, that is, it allocates only 1 byte on the stack. Your intention is probably to read the entire string into it, which is wrong. That is why I replaced it with an array of characters (limited to 99 characters + null terminating character \0): char cName[100];
scanf("%c", &cName); reads only one character into your cName variable.
You should use the %s format to scan the entire string, and store it into an array (big enough to store all input characters), by passing the address to a newly created variable cName.
printf("Good evening %c", cName); it prints out a single character, same as with scanf you should use %s instead to print out a sequence of characters from your array until a null terminating character (\0) is encountered.
Why for the single character &cName was necessary as 2nd argument, and now just cName for a string ?
This is because we have changed the formatting string and the destination that scanf initializes. That is, scanf needs to initialize your variable. Your variable is allocated on the stack and what you need to do is to pass its address, so that the scanf can set its value in an indirect manner (through pointer).
In C, to get the address of a variable, whe normally use an ampersand - &. However, in case of arrays (declared like char cName[500];), the name of the array is itself a pointer to its first character. Alternatively, you could use a syntax like &cName[0] to obtain the address of the first character.
This is why in case of a single character we would say &cName to get its address, and in case of arrays we say simply cName.
It is possible to overflow cName array!
Yes it is! Your array is now only [100]-character-long. Some hacker could now type in more characters, possibly overriding your stack. How can we make it more safe? Very easily! We can limit the length of the string being copied to the array within scanf itself by proving length-specifier, just like below:
scanf("%99s", cName);
Hold on a second! My array is 100-character-long, why do you allow only 99 in scanf ?
If you want to treat your char-array as a string of characters, it should be terminated with (that is, the last character should be set to) a null terminating character, expressed by the \0 constant. And scanf appends this character after all input symbols have been read. This last 100th character is reserved for this purpose.
Should I provide %99s to printf just like to scanf ?
No! %s is sufficient. When printf sees the %s format, it knows to print out all characters until a null terminating character is encountered. Additionally, a length-specifier in case of printf is in fact a minimum-width-specifier, which allots a space for the argument in the output stream.
you can also declare a character pointer
char *cName;
cName = malloc(20*sizeof(*cName));
replace your scanf with this
scanf("%s", cName);
add this together with other header files
#include <stdlib.h>
and free the dynamic memory after you're done using it
free(cName);
I believe that you must use %s instead of %c. As %c will only scan/print one character. %s should scan/print multiple characters. You will also probably need to use strings. try:
#include <stdio.h>
#include <string.h>
int main ()
{
char cName [200];
scanf("%s",&cName);
printf("Good evening %s.\n",cName);
return 0
}

Resources