How does scanf handle white space? - c

I am new to Cstring, sorry for asking dumb question, please help.
char string[10];
printf("Give me your last name:\n");
scanf ("%s", string); //if i type 123 123
printf("Original string:%s\n", string); //it shows 123
Compare to:
char string[] = "123 123";
printf("Original string:%s\n", string); //it shows 123 123

The problem is scanf. When you use scanf with %s it reads until it hits whitespace.
To read the entire line you may want to consider using fgets().

scanf treats space as the end of the string.
So it stops reading once it encounters a space character.
While in case of c style array \0 is considered the end of the array.
When you initialize,
char string[] = "123 123";
A \0 is implicitly added at the end of 7th character 3, and it marks the end of this array.

scanf scans for a string until any whitespace. If you need to read until a newline, then use fgets():
char string[64];
fgets(string, sizeof(string), stdin);
printf("%s", string); // should now contain any whitespace in the string.

This is the right behaviour. By default scanf reads the string until it reaches a whitespace.
From scanf manpage:
s
Matches a sequence of non-white-space characters;
the next pointer must be a pointer to character array that is long enough
to hold the input sequence and the terminating
null character ('\0'), which is added automatically.
The input string stops at white space or at the maximum field width,
whichever occurs first.

Sometimes gets() also creates some warnings. If you are so determined to use scanf(), you can use it like scanf("%[^\n]", character_array_variable);
#include<stdio.h>
#include<string.h>
void main()
{
char FullName[20];
scanf("%[^\n]s",FullName);
printf("%s",FullName);
}
The output image:

What you see here happens due to the way scanf() works. You can print strings with any character inside (no matter whether it's a whitespace or not). scanf() just stops once it encounters a whitespace as it considers this variable "done".

Related

Meaning of asterisk in scanf format specifier?

What does
scanf("%[^\n]%*c", s); and
scanf("%[^\n]", s); mean?
What is the difference?
Please see the specifications for scanf, in particular for the format specifiers.
The first statement, scanf("%[^\n]%*c", s);, reads any characters into the variable called s of type char* until there is a newline character (\n) in the input. This newline character is not read and will remain in the input. It will append a null-terminator (\0) to the string read into s. This concludes the "string reading" part.
The function scanf will then read any character, which in this case must be the newline character, but will not assign its value to any variable as the asterisk (*) is the "assignment-suppressing character *".
The second statement, scanf("%[^\n]", s);, is identical to the first, except that it will leave the newline character in the input.
Note that the standard warnings for using scanf apply here. This function is tricky to use safely. When reading a string, always include a maximum number of characters to read to avoid buffer overflow attacks. E.g. scanf(%16[^\n]", s. This will read a maximum of 15 characters, as it does not include the null-terminator.
scanf("%[^\n]%*c", s);
means to scanf that the field length is provided by an extra parameter in the parameter list, just before the string, so to be correct, it should read as
void f(char *buffer, size_t buffer_sz)
{
scanf("%[^\n]%*c", buffer_sz, buffer);
}
(but I'm afraid that you are going to skip over anything that is not a \n, which probably is not what you wantk, I think that format is incorrect, as you didn't specify the main letter specifier)

C Program won't terminate after compared to empty string

I am attempting to terminate my C program by checking for an empty string ("") but it seems not to work. I have tried to compare to "\0" as well but it was to no avail.
#include <stdio.h>
#include <string.h>
int main(void) {
char nameInput[128];
for(;;) {
printf("Enter nation name: ");
scanf("%s", nameInput);
if(!strcmp(nameInput, "")){
break;
}
printf("Got nation named \"%s\"\n", nameInput);
}
printf("All done getting nations!\n");
return 0;
}
The "%s" specifier in scanf("%s", nameInput); first consumes1 and discards leading white-space including all '\n' from the Enter before scanning and saving to nameInput.
That is why repeated entries of empty lines do not advance the scan. "%s" is waiting for some non-white-space input.
A better alternative to scanf() is to read all user input with fgets() and then parse the string.
fgets() reads a line and saves the result as a string - usually including the line's ending '\n'.
// scanf("%s", nameInput);
if (fgets(nameInput, sizeof nameInput, stdin)) {
// Success at reading input.
nameInput[strcspn(nameInput, "\n")] = '\0'; // lop off the potential trailing \n
if(!strcmp(nameInput, "")){ // or simply `if(nameInput[0] == '\0')
break;
}
...
have tried to compare to "\0" as well but it was to no avail.
if(!strcmp(nameInput, "")) and if(!strcmp(nameInput, "\0")) do the same thing. strcmp() is comparing strings.
"" is a string literal of 1 char: the null character.
"\0" is a string literal of 2 char: two null characters.
The string compare stops at the first null character.
"%s" by itself also lacks a width limit. Code has no safe guard against input like "BlahBlah...(120_some_more)Blah" and can lead to undefined behavior due a buffer overrun of char nameInput[128];. Code could use "%127s" to prevent that, yet that only handles one of the short-comings of scanf().
1
Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a [, c, or n specifier. C17dr § 7.21.6.2 8
It's not that it won't terminate, it is awaiting the input that wasn't (yet) typed in.
scanf is not using the right pattern string to scan in anything (including nothing) before the carriage return. You'll need to look into scanf patterns, and alter your pattern from "%s" to something that scanf will accept as input.
If you test out your program, you will see that after pressing "enter" you can type in a word and press enter again, and since you now have a word in the input, the scanf picks it up (discarding the whitespace, as it should with "%s").

What does scanf("%[^\n]%*c", &s); actually do?

I know for a fact that [^\n] is a delimiter that makes scanf to scan everything until "enter" key is hit. But I don't know what the remainder of "%[^\n]%*c" is used for. Also why do we have to mention "&s" instead of "s" in the scanf function.
I tried running this:
char s[100];
scanf("%[^\n]s",s);
scanf("%[^\n]s",&s);
Both the above scanf statements worked exactly the same for me. If there is any difference between them, What is it?
Also Why should I prefer scanf("%[^\n]%*c", &s); to the above declarations?
scanf("%[^\n]s",&s); has many troubles:
No check of return value.
No overrun protection
No consumption of '\n'
No assignment of s on '\n' only
No need for s in "%[^\n]s"
Wrong type &s with format.
scanf("%[^\n]s",s); only has one less problem (last one).
Use fgets(s, sizeof s, stdin)
if (fgets(s, sizeof s, stdin)) {
s[strcspn(s, "\n")] = 0; // Lop off potential ending \n
// Success
} else {
// failure
}
No check of return value.
Without checking the return value, success of reading is unknown and the state of s indeterminate.
No overrun protection
What happens when the 100th character is inputted? - "Very bad. Not good. Not good." Necron 99, Wizards 1977
No consumption of '\n'
'\n' remains in stdin to foul up the next read.
No assignment of s on '\n' only
If the input begin with '\n', scanf() returns and s unchanged. It is not assigned "".
No need for "s" in "%[^\n]s"
The "s" is not part of the specifier "%[^\n]". Drop it.
Wrong type &s with format.
%[...] matches a char *, not a pointer to a char[100] like &s (UB).
You can take a string as input in C using scanf(“%s”, s). But, it accepts string only until it finds the first space.
In order to take a line as input, you can use scanf("%[^\n]%*c", s); where s is defined as char s[MAX_LEN] where MAX_LEN is the maximum size of s . Here, [] is the scanset character. ^\n stands for taking input until a newline isn't encountered. Then, with this %*c, it reads the newline character and here, the used * indicates that this newline character is discarded.
Note: After inputting the character and the string, inputting the sentence by the above mentioned statement won't work. This is because, at the end of each line, a new line character (\n) is present. So, the statement: scanf("%[^\n]%*c", s); will not work because the last statement will read a newline character from the previous line. This can be handled in a variety of ways and one of them being: scanf("\n"); before the last statement.

scanf and strcmp with c string

I found a nice example of how to use strcmp, but it's only working with fgets(), and i need to make it work with scanf. So, here's the code:
int main(void) {
char fruit[] = "apple\n";
char ans[80];
do {
printf ("Guess my favorite fruit? ");
scanf ("%s",ans);
} while (strcmp (fruit, ans) != 0);
puts ("Correct answer!");
return 0;
}
Even when I write the correct answear ("apple") it stays in the loop and keeps asking me what is the favorite fruit... I'm guessing it has something to do with the chars that are not written at ans[80](I need it to be a char array with 80chars at max). I'm not getting this...
Thanks in advance.
Scanf will ignore "\n", so you should init char fruit[] = "apple", since ans will never end with '\n'.
P.S: An explain for scanf: Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence.
scanf() does not write the trailing newline character(s) into ans. strcmp() does consider newline characters in its comparison, so it's not matching your literal, which includes the newline.
"scanf" does take newline "\n" character as input. So you are not able to equal the both strings. if you want to equal both strings you need to remove newline"\n" form the first string ("apple" is fine).

strlen() returns the string count with the null terminator

normally, strlen() does not count the null terminator at the end of the string. But, below code prints the string count with the null terminator. Can anyone explain me why? Thanks
char str2[100];
printf("\nEnter a string: ");
fgets (str2, sizeof(str2), stdin);
printf("\n%d",strlen(str2));
I am assuming the preceding fgets prompt picked up the newline character.
For example:
You put in apple.
Internally your string was stored as apple\n\0.
strlen then returned 6 for apple + '\n'
The fgets() function accepts the input when a newline character(Enter key when using stdin) is encountered, and the newline character \n is considered a valid character by the function and included in the string copied to your str2.Hence when you pass it as a parameter to strlen() it gives one more than the original number of characters in your string to account for the additional \n character.
If you want the original number of characters or don't want a \n to be added, use the gets() function as it doesn't copy the newline character.And further, you only need to pass the string as argument,no need to pass the stream (stdin) as the default stream for gets() is stdin.
char str2[100];
printf("\nEnter a string: ");
gets(str2);
printf("\n%d",strlen(str2));
Here you have used fgets() function to take input. When you take input by fgets() function then an additional new line character('\n') will be added with your sting. suppose your input is : "hello" . after typing this sting you must press ENTER key for which new line character will be added with your string. Hence its seems to you that strlen() counts the null terminator. But if you take input using scanf() function it will not add additional new line character('\n') when ENTER is pressed. So you will see the exact number of character you string contains. Run the following code to see my explanation.
#include<stdio.h>
#include<string.h>
void main()
{
char str2[100];
printf("\nEnter a string: ");
scanf("%s",str2);
//fgets (str2, sizeof(str2), stdin);
printf("\n%d",strlen(str2));
}
as stated by others, the fgets() will read the newline(\n) character and store it in your array.
after every call to fgets() I always use strcspn() to search the array/pointer to find the newline character and replace it with the null character.
char str2[100];
printf("\nEnter a string: ");
fgets (str2, sizeof(str2), stdin);
//new line of code to replace '\n' with '\0'
str2[strcspn(str2, "\n")] = '\0';
printf("\n%d",strlen(str2));
fgets() reads until \n is encountered.
If the user enters anshul then str2 will contain anshul\n\0.
strlen() will return 7 because strlen() searches until it finds the NULL('\0') character.
gets(s) does not include the '\n' when you hit the enter key after being done entering the string.But, fgets() does include the '\n' while reading from a file.
As per the man page(use: man fgets) on linux terminal,
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a
newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.

Resources