Different behaviour of scanf - c

Is there some difference between
scanf("%s", c);
and
scanf(" %s", c);
Like can it affect my program in any way?
Thanks in advance.

The scanf %s conversion specifier skips leading whitespace characters and parses a word up to and not including subsequent whitespace.
Adding a space in front of the %s has no effect, it is fully redundant.
The same holds for %d and %f but not %c or %[.
Note also that the %s and %[ specifiers are risky since you do not provide scanf any limit for the number of characters to store into the destination. This might be OK for sscanf() as the conversion is implicitly limited by contents of the source string, but must be avoided for scanf and fscanf.
You can provide a numeric argument between the % and the s for the maximum number of chars to store before the null terminator:
char buffer[100];
if (scanf("%99s", buffer) == 1) {
/* a word was parsed correctly into buffer */
}

Related

Scanf reads wrong value

I’m new to C programming language, I have wrote a simple code that reads two “char” values and prints them on the screen but the second one got empty value for a strange reason. What’s going wrong with my code?
Char c;
Scanf(“%c”,&c);
Printf(“Value:%c”,c);
Scanf(“%c”,&c);
Printf(“Value:%c”,c);
Output:
Value:g
Value:
(This is a comment, but comments are hard to format)
There's nothing wrong with your code (other than the failure to check the value returned by scanf and deal with errors or incorrect input). Consider:
#include <stdio.h>
int
main(void)
{
char c;
scanf("%c",&c);
printf("Value:%c",c);
scanf("%c",&c);
printf("Value:%c",c);
return 0;
}
$ gcc a.c
$ printf 'abc' | ./a.out
Value:aValue:b
Perhaps what's "wrong" is that you have newlines in your input. (eg, you are entering data interactively and forgetting that when you hit "return" a newline is inserted into the input stream.)
If your goal was to read two "interesting" characters, and if you don't think that whitespace characters like space and newline are "interesting", you've been tripped up by number six out of the seventeen things about scanf that are designed to trip up the unwary: %c does read whitespace characters.
If you want scanf to skip over whitespace characters, so that %c will read the next, non-whitespace or "interesting" character, simply include a space character in the format string before the %c:
char c;
scanf(" %c", &c);
printf("Value: %c\n",c);
scanf(" %c", &c);
printf("Value: %c\n",c);
In a scanf format string, the presence of a whitespace character indicates that you want scanf to skip over all whitespace at that point in the input.
Normally you don't have to worry about skipping whitespace with scanf, because most of the other format specifiers -- %d, %f, %s, etc. -- automatically skip over any whitespace, if necessary, before they start parsing their input. But %c is special: someone thought you might want to use it to read whitespace characters, so it doesn't skip them, so if you don't want to read them, you have to skip them yourself, with that space character in the format string first.

Why is scanf failing to read inputs correctly?

I cant figure out whats wrong. Am i using format specifiers in wrong way? Someone please help i am very new to coding.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[20];int age;char grade;double gpa;char area[10];
printf("User Input\n");
printf("Enter your name: ");
fgets(name,20,stdin);
printf("Your name is %s\n",name);
printf("Enter your age: ");
scanf("%d\n",&age);
printf("Your age is %d\n",age);
printf("Enter you grade: ");
scanf("%c\n",&grade);
printf("Your grade is %c\n",grade);//Why is this giving an int output?
printf("Enter your gpa: ");
scanf("%f\n",&gpa);
printf("Your gpa is %f\n",gpa);
printf("Enter your area: ");
scanf("%s\n",&area);
printf("Your area is %s",area);//This shows grade input
return 0;
}
Output
You use fgets correctly when reading name. I'd recommend also using fgets for all your other inputs, and then parsing the intended values out of them. For example:
char age_str[20];
fgets(age_str, 20, stdin);
age = strtol(age_str, NULL, 10);
This is preferable to using scanf directly for non-string inputs since if input fails to match a format string, it will remain in stdin and screw up the other scanf calls.
If you would like to use scanf correctly:
Check its return value to see if it matches the number of format specifiers in the string. If not, some inputs were not successfully read. You may want to use a do/while loop for this.
Begin your format strings with a space, as in " %c", so that any whitespace remaining in stdin will be skipped over.
Don't end your format strings with a newline.
Some things to remember about scanf:
Most conversion specifiers like %s, %d, and %f will skip over leading whitespace - %c and %[ will not. If you want to read the next single non-whitespace character, use " %c" - the leading blank tells scanf skip over any leading whitespace before reading the next non-whitespace character;
For what you are trying to do, you should not use \n in your format strings - it will cause scanf to block until you enter a non-whitespace character;
You do not need to use the & operator on array expressions like area; under most circumstances, array expressions are converted to pointer expressions1. Honestly, you should read area the same way you read name, using fgets (and you should always check the result of fgets), or you should specify the maximum field width in the specifier: scanf( "%9s", area ); (a 10-element array can hold up to a 9-character string, since one element has to be reserved for the string terminator);
You should get in the habit of checking the result of scanf - it will return the number of successful conversions and assignments. For example, scanf( "%d %d", &x, &y ) will return 2 if both x and y are read successfully. It will return EOF if end-of-file is signaled or there's a read error.
scanf will read up to the next character that doesn't match the conversion specifier - IOW, if you're using %d, then scanf will skip over any leading whitespace, then read up to the next character that isn't a decimal digit. That character is left in the input stream. This means if you're using %d and type in 123e456, scanf will read up to that 'e' character and assign 123 to the target. If you try to read again with %d, scanf will immediately stop reading on that e and return 0 without assigning anything to the target (this is called a matching failure). This will continue until you remove that 'e' from the input stream (such as with getchar or fgetc or scanf with the %c specifier, etc.
You need to make sure the types of the arguments match the format specifier. %s expects an argument of type char *, %d expects int *, %f expects float *. %x expects unsigned int *, %lf expects double *, etc.
This is one of the "deeply unintuitive" aspects of C I was talking about in my comment.

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 );

Difference between return values of scanf("%s", str) and scanf("%[^\n]s", str)

while(scanf("%s",a))
{
if(a[0]=='#')
break;
nextpermutation(a);
}
This while loop perfectly works for scanf("%s", a)
but if I put scanf("%[^\n]s", a) in the while it just runs for one time.
I checked the return value of both scanf and they were the same still.
I didn't get why this is happening...
The battle of two losers: "%s" vs. "%[^\n]s":
Both specifiers are bad and do not belong in robust code. Neither limits user input and can easily overrun the destination buffer.
The s in "%[^\n]s" serves no purpose. #Jonathan Leffler. So "%[^\n]" is reviewed following - still that is bad.
"%s" consumes leading white-space. "%[^\n]" does not #Igor Tandetnik
"%[^\n]" reads nothing if the leading character is a '\n', leaving a unchanged or rarely in an unknown state.
"%[^\n]" reads all characters in except '\n'.
"%s" reads all leading white-space, discards them, then reads and saves all non-white-spaces.
Better to use fgets() to read a line of user input and then parse it.
Example: to read a line of input, including spaces:
char a[100];
if (fgets(a, sizeof a, stdin) == NULL) Handle_EOF_or_Error();
// if the potential trailing \n is not wanted
a[strcspn(a, "\n")] = '\0';
What is that s doing there in %[^\n]s?
Your scanf("%[^\n]s") contains two format specifiers in its format string: %[^\n] followed by a lone s. This will require the input stream to contain s after the sequence matched by %[^\n]. This is, of course, is not very close in behavior to plain %s. Matching sequences for these formats will be very different for that reason alone.
Note that [] is not a modifier for %s format, as you seem to incorrectly believe. [] in scanf is a self-sufficient format specifier. Did you mean just %[^\n] by any chance instead of %[^\n]s?

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).

Resources