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

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.

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)

Difference between `"%[^\n]"` and `"%[^\n]\n"` and `"%[^\n]%*c"` in C how do they work?

I could not find the answer anywhere else.
%[^\n] - When I run this one, scanf is getting input and terminating after I press enter. ( Probably leaving \n in the input system)
%[^\n]\n - this one is getting the input but scanf is NOT terminating immediately after I press enter like the one above. I hit more enter and it makes more newlines. When I give a character and then press enter it finally terminates. Example:
int main(void)
{
char s[100];
scanf("%[^\n]\n", s);
printf("%s", s);
return 0;
}
The results:
Last one:
%[^\n]%*c - When I give some input and press enter. scanf immediately terminates.
How do those 3 work and how do they differ?
All 3 format begin with "%[^\n]".
"%[^\n]" is poor code2 that lacks a width limit and is susceptible to buffer overrun. Use a width limit like "%99[^\n]" with char s[100];.
"%[...]" does not consume leading whitespace like many other specifiers.
This specifier directs reading input until a '\n'1 is encountered. The '\n' is put back into stdin. All other characters are saved in the matching destination's array s.
If no characters were read (not counting the '\n'), the specifier fails and scanf() returns without changing s - rest of the format is not used. No null character appended.
If characters were read, they are saved and a null character is appended to s and scanning continues with the next portion of the format.
"\n" acts just like " ", "\t", "any_white_space_chracter" and reads and tosses 0 or more whitespace characters. It continues to do so until a non-white-space1 is read. That non-whitespace character is put back into stdin.
Given line buffered input, this means a line with non-whitespace following input is needed to see the next non-whitespace and allow scanf() to move on.
With scanf(), a "\n" at the end of a format is bad and caused OP's problem.
"%*c" reads 1 character1 and throws it away - even if it is not a '\n'. This specifier does not contribute to the return count due to the '*'.
A better alternative is to use fgets().
char s[100];
if (fgets(s, sizeof s, stdin)) {
s[strcspn(s, "\n")] = '\0'; // To lop off potential trailing \n
A lesser alternative is
char s[100] = { 0 };
scanf("%99[^\n]", s);
// With a separate scanf ....
scanf("%*1[\n]"); // read the next character if it is a \n and toss it.
1 ... or end-of-file or rare input error.
2 IMO, worse than gets().

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

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?

Why %[^\n]s does not work in loop?

I was writing a code to take a space-separated string input for a multiple time in between a loop. I saw that %[^\n]s did not work in loop but %[^\n]%*c did. My question is why %[^\n]sdid not work. Here is my code:
#include<stdio.h>
main(){
while(1){
char str[10];
scanf("%[^\n]s",str);
printf("%s\n",str);
}
return 0;
}
The format specifier %[^\n] means "read a string containing any characters until a newline is found". The newline itself is not consumed. When the newline is found, it is left on the input stream for the next conversion.
The format specifier %*c means "read exactly one character and discard it".
So the combination
scanf( "%[^\n]%*c", str );
means "read a string up to the newline character, put the string into the memory that str points to, and then discard the newline".
Given the format %[^\n]s, the s is not part of the conversion specifier. That format says "read characters until a newline is found, and then the next character should be an s". But next character will never be an s, because the next character will always be the newline. So the s in that format serves no purpose.
%[^\n] does not look for space-separated strings. It reads the whole line up until (but not including) the newline.
If your line contains more than 9 characters this causes undefined behaviour by overflowing the buffer.
The buffer overflow could be prevented by writing %9[^\n] but then you have a new issue: on a longer line, %*c would discard the 10th character and the next scan would keep reading from that same line.
Another complicating factor is that if your file contains a blank line, then %[ considers it a matching failure. That means scanf stops, so it does not go on to process %*c. In this case, the newline is not consumed, and the output buffer is not written to at all.
Your code also goes into an infinite loop because you never break out of while(1).
This code shows correct use of ^[:
while (1)
{
str[0] = '\0'; // In case of matching failure
scanf("%9[^\n]", str); // read as much of the line as we can
scanf("%*[^\n]"); // discard the rest of the line
if ( getchar() == EOF ) // discard the newline
break; // exit loop when we finished the input
printf("%s\n", str);
}
If you really do want to read space-separated text then you could use %9s instead of %9[^\n] in my above example. That introduces a new difference though: %s skips over a blank line.
If that's OK then that's OK. If you want to not skip blank lines, then you could use my code above, but add in at the end:
char *p = strchr(str, ' ');
if ( p )
*p = '\0';
Robust string input is difficult in C!

Resources