unknown error the function 'scanf("%[^\n]%*c", &sent);' - c

Getting to point, I am a beginner in the C language and just encountered a weird method of inputting string in a C Program:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main() {
char ch, string[100], sent[100];
scanf("%c", &ch);
scanf("%s", &string);
scanf("%[^\n]%*c", &sent);
printf("%c\n", ch);
printf("%s\n", string);
printf("%s", sent);
return 0;
}
Here is the error: the last line (Sentence) doesn't print, no idea where I am wrong, but on research I found this code:
scanf(" %[^\n]%*c", &sent); //not theres a space before %[^\n]%*c; and then it worked (wtf)
can you explain why it worked by just adding a space there.

The space () in the format string causes scanf to skip whitespace in the input. Its normally not needed, as most scanf conversions ALSO skip whitespace before they scan anything, but the two that do not are %c and %[ -- so using a space before %[ has a visible effect. Lets look at what your 3 scanf calls do:
scanf("%c",&ch); // read the next character into 'ch'
scanf("%s",&string); // skip whitespace, then read non-whitespac characters
// into 'string', stopping when the first whitespace after
// some non-whitespace is reached (that last whitespace
// will NOT be read, being left as the next character
// of the input.)
scanf("%[^\n]%*c",&sent); // read non-newline characters into 'sent', up until a
// newline, then read and discard 1 character
// (that newline)
So the 3rd scanf will start reading with the whitespace that ended the second scanf. If you add a space to the start of the format, it will instead read and discard whitespace until it finds a non-whitespace character, then start reading into sent with that non-whitespace character.
Also of interest is what happens if the whitespace that ends the second scanf happens to be a newline. In that case, the thrid scanf will fail completely (as there are no non-newline characters to be read before the newline) and do nothing. Adding the space here to the third scanf ensures that it does not fial due to the newline (it will be discarded as whitespace), so it will always read something into sent, unless an EOF is reached.

Related

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

I got an error reading a character on C [duplicate]

This question already has answers here:
Why does scanf ask twice for input when there's a newline at the end of the format string?
(7 answers)
Closed 5 years ago.
I want to read a single character from the console, but when I do, the program reads characters yet and I must write another character to save the first and finish its execution.
Code:
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
char peps;
int main(int argc, char const *argv[]) {
printf("write a character:\n");
scanf(" %c\n", &peps);
printf("%c\n", peps);
return 0;
}
Can anyone explain why it does that and how to correct this error?
If you remove the \n from the scanf(), it should work as you want.
ie, do
scanf(" %c", &peps);
instead of
scanf(" %c\n", &peps);
This is because the \n in the scanf() format string is telling the computer to read and ignore all white spaces (including \n) after reading a character.
So all white spaces including the newlines given by typing the enter key, would be ignored. This will stop only when a non-white space character is encountered which won't be read and would thus remain in the input buffer.
So, in your case, a character would first be read and it would wait for a non-white space character before executing the printf() following the scanf(). The non-white space character would remain in the input buffer and was not read and is hence not printed at once. It would be read only upon reading from the stdin again.
If you want to explore this further, consider placing that scanf() and printf() in a loop and examine the output.
Note that replacing that \n with a space would have the same effect.
ie,
scanf(" %c\n", &peps);
and
scanf(" %c ", &peps);
would have the same behavior.
What's the behavior of scanf when the format string ends with a newline?
Behaviour of scanf when newline is in the format string

%[^\n]s in scanf does not wait for input and gets skipped

In the loop in the code below, scanf("%[^\n]s",array) is not working. It does not wait for input and gets skipped. But a space before % fixes the issue. Why?
Here is the wrong program:
#include <string.h>
#include <stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--){
char arr[199];
scanf("%[^\n]s",arr);
printf("%s",arr);
}
return 0;
}
Here is right code:
#include <string.h>
#include <stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--){
char arr[199];
scanf(" %[^\n]s",arr);
printf("%s",arr);
}
return 0;
}
Why does it need a space before % for it to work as expected?
First of all, the trailing s is not part of the %[ format specifier, so remove it and lets talk about %[^\n].
Now, what %[^\n] tells scanf to do is scan everything until a newline character ('\n') or EOF, whichever comes first, and stores it in its corresponding argument, in this case, arr.
And here is the catch: %[^\n] fails if the first character to be read is a \n.
'Wait', you say. 'I did not type in a lone enter. So, why'd it fail?'. True. You did not. But remember the Enter you pressed for the previous line? Turns out, the previous call to scanf grabs everything until the \n, leaves the \n there and returns. So, in the next iteration of the loop, the scanf sees this \n character left over by the previous call to scanf, fails and returns 0.
As for the space, it is a whitespace character. And a whitespace character in scanf instructs it to scan and discard all whitespace characters until the first non-whitespace character. So, it removes the \n (since it is a whitespace character) and scanf will wait for further input.
With this format %[^\n], scanf() stops after reading a newline. So, after the first input, there's a newline left which isn't consumed.
So, subsequent scanf() calls don't read any input at all.
With a space in %[^\n], scanf() ignores any number of whitespace chars. Hence, scanf() ignores the leftover newlines. From scanf:
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.
By the way, you don't need that extra s at the end of the format string.
Use fgets() which is generally superior to scanf() (fgets() will read the newline char into the buffer if there's space). Also see: Why does everyone say not to use scanf? What should I use instead?.

Why won't this scanf format-string work? "%[^\n]\n"

I've seen a few examples where people give scanf a "%[^\n]\n" format string to read a whole line of user input. If my understanding is correct, this will read every character until a newline character is reached, and then the newline is consumed by scanf (and not included in the resulting input).
But I can't get this to work on my machine. A simple example I've tried:
#include <stdio.h>
int main(void)
{
char input[64];
printf("Enter some input: ");
scanf("%[^\n]\n", input);
printf("You entered %s\n", input);
}
When I run this, I'm prompted for input, I type some characters, I hit Enter, and the cursor goes to the beginning of the next line but the scanf call doesn't finish.
I can hit Enter as many times as I like, and it will never finish.
The only ways I've found to conclude the scanf call are:
enter \n as the first (and only) character at the prompt
enter Ctrl-d as the first (and only) character at the prompt
enter some input, one or more \n, zero or more other characters, and enter Ctrl-d
I don't know if this is machine dependent, but I'm very curious to know what's going on. I'm on OS X, if that's relevant.
According to the documentation for scanf (emphasis mine):
The format string consists of whitespace characters (any single whitespace character in the format string consumes all available consecutive whitespace characters from the input), non-whitespace multibyte characters except % (each such character in the format string consumes exactly one identical character from the input) and conversion specifications.
Thus, your format string %[^\n]\n will first read (and store) an arbitrary number of non-whitespace characters from the input (because of the %[^\n] part) and then, because of the following newline, read (and discard) an arbitrary number of whitespace characters, such as spaces, tabs or newlines.
Thus, to make your scanf stop reading input, you either need to type at least one non-whitespace character after the newline, or else arrange for the input stream to end (e.g. by pressing Ctrl+D on Unix-ish systems).
Instead, to make your code work as you expect, just remove the last \n from the end of your format string (as already suggested by Umamahesh P).
Of course, this will leave the newline still in the input stream. To get rid of it (in case you want to read another line later), you can getc it off the stream, or just append %*c (which means "read one character and discard it") or even %*1[\n] (read one newline and discard it) to the end of your scanf format string.
Ps. Note that your code has a couple of other problems. For example, to avoid buffer overflow bugs, you really should use %63[^\n] instead of %[^\n] to limit the number of characters scanf will read into your buffer. (The limit needs to be one less than the size of your buffer, since scanf will always append a trailing null character.)
Also, the %[ format specifier always expects at least one matching character, and will fail if none is available. Thus, if you press enter immediately without typing anything, your scanf will fail (silently, since you don't check the return value) and will leave your input buffer filled with random garbage. To avoid this, you should a) check the return value of scanf, b) set input[0] = '\0' before calling scanf, or c) preferably both.
Finally, note that, if you just want to read input line by line, it's much easier to just use fgets. Yes, you'll need to strip the trailing newline character (if any) yourself if you don't want it, but that's still a lot easier and safer that trying to use scanf for a job it's not really meant for:
#include <stdio.h>
#include <string.h>
void chomp(char *string) {
int len = strlen(string);
if (len > 0 && string[len-1] == '\n') string[len-1] = '\0';
}
int main(void)
{
char input[64];
printf("Enter some input: ");
fgets(input, sizeof(input), stdin);
chomp(input);
printf("You entered \"%s\".\n", input);
}
Whitespace characters in format of scanf() has an special meaning:
Whitespace character: the function will read and ignore any whitespace
characters encountered before the next non-whitespace character
(whitespace characters include spaces, newline and tab characters --
see isspace). A single whitespace in the format string validates any
quantity of whitespace characters extracted from the stream (including
none).
Thus, "%[^\n]\n" is just equivalent to "%[^\n] ", telling scanf() to ignore all whitespace characters after %[^\n]. This is why all '\n's are ignored until a non-whitespace character is entered, which is happened in your case.
Reference: http://www.cplusplus.com/reference/cstdio/scanf/
Remove the the 2nd new line character and the following is sufficient.
scanf("%[^\n]", input);
To answer the original one,
scanf("%[^\n]\n", input);
This should also work, provided you enter a non white space character after the input. Example:
Enter some input: lkfjdlfkjdlfjdlfjldj
t
You entered lkfjdlfkjdlfjdlfjldj

Reading newline from previous input when reading from keyboard with scanf()

This was supposed to be very simple, but I'm having trouble to read successive inputs from the keyboard.
Here's the code:
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
scanf ("%s", string);
printf ("%s", string);
printf ("\nwrite a character: ");
scanf ("%c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
What is happening
When I enter a string (e.g.: computer), the program reads the newline ('\n') and puts it in character. Here is how the display looks like:
write something: computer
computer
Character:
Correspondent number: 10
Moreover, the program does not work for strings with more than one word.
How could I overcome these problems?
First scanf read the entered string and left behind \n in the input buffer. Next call to scanf read that \n and store it to character.
Try this
scanf (" %c", &characte);
// ^A space before %c in scanf can skip any number of white space characters.
Program will not work for strings more than one character because scanf stops reading once find a white space character. You can use fgets instead
fgets(string, 200, stdin);
OP's first problem is typically solved by prepending a space to the format. This will consume white-space including the previous line's '\n'.
// scanf("%c", &character);
scanf(" %c", &character);
Moreover, the program does not work for strings with more than one word. How could I overcome these problems?
For the the 2nd issue, let us go for a more precise understanding of "string" and what "%s" does.
A string is a contiguous sequence of characters terminated by and including the first null character. 7.1.1 1
OP is not entering a string even though "I enter a string (e.g.: computer)," is reported. OP is entering a line of text. 8 characters "computer" followed by Enter. There is no "null character" here. Instead 9 char "computer\n".
"%s" in scanf("%s", string); does 3 things:
1) Scan, but not save any leading white-space.
2) Scan and save into string any number of non-white-space.
3) Stop scanning when white-space or EOF reached. That char is but back into stdin. A '\0' is appended to string making that char array a C string.
To read a line including spaces, do not use scanf("%s",.... Consider fgets().
fgets(string, sizeof string, stdin);
// remove potential trailing \r\n as needed
string[strcspn(string, "\n")] = 0;
Mixing scanf() and fgets() is a problem as calls like scanf("%s", string); fgets(...) leave the '\n' in stdin for fgets() to read as a line consisting of only "\n". Recommend instead to read all user input using fgets() (or getline() on *nix system). Then parse the line read.
fgets(string, sizeof string, stdin);
scanf(string, "%c", &character);
If code must user scanf() to read user input including spaces:
scanf("%*[\n]"); // read any number of \n and not save.
// Read up to 199 `char`, none of which are \n
if (scanf("%199[^\n]", string) != 1) Handle_EOF();
Lastly, code should employ error checking and input width limitations. Test the return values of all input functions.
What you're seeing is the correct behavior of the functions you call:
scanf will read one word from the input, and leave the input pointer immediately after the word it reads. If you type computer<RETURN>, the next character to be read is the newline.
To read a whole line, including the final newline, use fgets. Read the documentation carefully: fgets returns a string that includes the final newline it read. (gets, which shouldn't be used anyway for a number of reasons, reads and discards the final newline.)
I should add that while scanf has its uses, using it interactively leads to very confusing behavior, as I think you discovered. Even in cases where you want to read word by word, use another method if the intended use is interactive.
You can make use of %*c:
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
scanf ("%s%*c", string);
printf ("%s", string);
printf ("\nwrite a character: ");
scanf ("%c%*c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
%*c will accept and ignore the newline or any white-spaces
You cal also put getchar() after the scanf line. It will do the job :)
The streams need to be flushed. When performing successive inputs, the standard input stream, stdin, buffers every key press on the keyboard. So, when you typed "computer" and pressed the enter key, the input stream absorbed the linefeed too, even though only the string "computer" was assigned to string. Hence when you scanned for a character later, the already loaded new line character was the one scanned and assigned to character.
Also the stdout streams need to be flushed. Consider this:
...
printf("foo");
while(1)
{}
...
If one tries to execute something like this then nothing is displayed on the console. The system buffered the stdout stream, the standard output stream, unaware of the fact it would be encounter an infinite loop next and once that happens, it never gets a chance to unload the stream to the console.
Apparently, in a similar manner whenever scanf blocks the program and waits on stdin, the standard input stream, it affects the other streams that are buffering. Anyway, whatsoever may be the case it's best to flush the streams properly if things start jumbling up.
The following modifications to your code seem to produce the desired output
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
fflush(stdout);
scanf ("%s", string);
fflush(stdin);
printf ("%s", string);
printf ("\nwrite a character: ");
fflush(stdout);
scanf ("%c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
Output:
write something: computer
computer
write a character: a
Character a Correspondent number: 97

Resources