Why does scanf fail but fgets works? - c

I am asking the user for input on whether they would like to quit the program or not. There are two snippets. One reads input using the scanf() function, while the second reads input using the fgets() function. Using scanf(), the program goes in an infinite loop. Using fgets(), the program performs as intended. Why does scanf() fail, and fgets() work? How can i correct it so that scanf() will work? Here is the code:
First is with scanf()
#include <stdio.h>
#include <string.h>
int main(void)
{
char yesNo[6];
printf("Enter [quit] to exit the program, or any key to continue");
scanf("%s", &yesNo[6]);
while (strcmp(yesNo,"quit\n") != 0)
{
printf("Enter [quit] to exit the program, or any to continue");
scanf("%s", &yesNo[6]);
}
return 0;
}
Second is with fgets()
#include <stdio.h>
#include <string.h>
int main(void)
{
char yesNo[6];
printf("Enter[quit] to exit the program, or any key to continue: ");
fgets(yesNo, 6, stdin);
while (strcmp(yesNo,"quit\n") != 0)
{
printf("Enter [quit] to exit the program, or any key to continue:");
fgets(yesNo, 6, stdin);
}
return 0;
}

The difference between scanf("%s") and fgets you've to keep in mind is the way they take in input.
%s instructs scanf to discard all leading whitespace characters and read in all non-whitespace characters until a whitespace character (or EOF). It stores all the non-whitespace characters in its corresponding argument, in this case, yesNo, and then leaves back the last whitespace character back into the standard input stream (stdin). It also NUL-terminates its corresponding argument, in this case yesNo.
fgets reads in all input until a newline character ('\n') or until the maximum number of characters to read passed in as the second argument minus one (for the NUL-terminator '\0') has been read (or until EOF) and all this input, including the \n, is stored in its first argument, yesNo here, and it is NUL-terminated.
So, if you have scanf("%s", yesNo); with an input of quit\n, yesNo will contain just quit and the \n will be left in the stdin. Since the strings "quit" and "quit\n" aren't the same, strcmp will not return zero and the while loop will continue to loop.
For fgets(yesNo, 6, stdin); with the input quit\n, yesNo will hold quit\n and the stdin will be empty. strcmp returns zero as both the strings "quit\n" and "quit\n" are equal, and execution comes out of the loop.

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

Scanning Only the First Character in C

I know that adding a space in front of %c in scanf() will scan my second character; however, if two letters were inputted in the first character, it will input the second letter into the second character. How do I scan a single character only?
#include <stdio.h>
int main(void)
{
char firstch, secondch;
printf("Enter your first character: ");
scanf("%c", &firstch);
printf("Enter your second character: ");
scanf(" %c", &secondch);
printf("\n Fisrt character : %c \n Second character : %c \n", firstch, secondch);
return 0;
}
This is my result after running:
Enter your first character: ab
Enter your second character:
First character : a
Second character : b
I only want to read the first character 'a', but the second letter 'b' was inputted right away before I enter my second character.
When you are reading a line of user-input, use a line-oriented input function like fgets() or POSIX getline(). That way the entire line of input is read at once and you can simply take the first character from the line. Say you read a line into the array used as buffer called buf, e.g.
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer to read each line into */
You can simply access the first character as buf[0], or since buf[0] is equivalent to *(but + 0) in pointer notation, you can simply use *buf to get the first character.
As a benefit, since all line-oriented functions read and include the '\n' generated by the user pressing Enter after the input, you can simply check if the first character is '\n' as a way of indicating end-of-input. The user simply presses Enter alone as input to indicate they are done.
Using a line-oriented approach is the recommended way to take user input because it consumes and entire line of input each time and what remains in stdin unread doesn't depend on the scanf conversion specifier or whether a matching failure occurs.
Using " %c%*[^\n]" is not a fix-all. It leaves the '\n' in stdin unread. That's why you need the space before " %c". Where it is insidious is if your next input uses a line-oriented function after your code reading characters is done. Unless you manually empty the '\n' from stdin, before your next attempted line-oriented input, that input will fail because it will see the '\n' as the first character remaining in stdin.
A short example using fgets() for a line-oriented approach would be:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer to read each line into */
for (;;) { /* loop continually */
fputs ("enter char: ", stdout); /* prompt for input */
/* read/validate line, break on EOF or [Enter] alone */
if (!fgets (buf, sizeof buf, stdin) || *buf == '\n')
break;
printf (" got: %c\n\n", *buf); /* output character read */
}
}
Where you simply take input continually isolating the first character as the value you want until the user presses Enter alone to break the read-loop.
Example Use/Output
$ ./bin/fgetschar
enter char: a
got: a
enter char: ab
got: a
enter char: a whole lot of stuff you don't have to deal with using fgets()
got: a
enter char: banannas
got: b
enter char: cantelopes
got: c
enter char:
Look things over and let me know if you have further questions.
Using a space before the %c will skip whitespace before scanning the next non-whitespace character. %c itself just scans a single character -- the next character in the input after whatever else was scanned or skipped previously.
So the question is, what do you want to do? Do you want to skip over all extraneous input on the line after the first character (up to newline?) fgets or scanf("%*[^\n]"); scanf("%c"); will do that (but be careful -- if firstch was itself a newline, this will skip the next line.) Do you want to check the input and make sure it is exactly one character on a line? If so, use fgets (not scanf) and check that the line read is exactly two characters (a character and a newline). Or perhaps you really want to read keystrokes without having the user hit Enter after esch one? That requires changing the input source setup, which is OS dependent.
I'm still new to C coding, and I've found a suitable answer to my problem by using scanf("%*[^\n]");
#include <stdio.h>
int main(void)
{
char firstch, secondch;
printf("Enter your first character: ");
scanf(" %c%*[^\n]", &firstch);
printf("Enter your second character: ");
scanf(" %c%*[^\n]", &secondch);
printf("\n First character : %c \n Second character : %c \n", firstch,
secondch);
return 0;
}
Results after running:
Enter your first character: ab
Enter your second character: c
First character : a
Second character : c
Thanks to #Eraklon #Chris Dodd #David C. Rankin

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

Reading of standard input with fgets not waiting for input

Having this piece of code:
int main(void)
{
char str[4];
do
{
if (fgets(str,sizeof(str),stdin) == NULL)
break;
printf("\n %s \n", str);
}while (strncmp(str,"q\n",sizeof("q\n")));
return 0;
}
if i type more than 4 characters, then two lines are displayed. if i type 123456 and then press enter, does input store ['1','2','\n','\0'] or ['1','2','3','\0']? hen the second time printf is reached if i only press enter key one time?. How i can avoid this behaviour? I would like type 123456 and then get:
1234
The reason why fgets is only reading partial input is because the str array is too small. You need to increase the buffer size of str array.
Also remember that fgets will pick up \n ( enter / return ) that you press after giving your input.
To get rid of the \n do this:
fgets(str,sizeof(str),stdin);
str[strlen(str)-1] = '\0';
There is one MAJOR issue with your while condition ... I am not sure what your are trying to do there but strcmp is used to see if two strings are the same or not ... what you are doing is trying to compare a string to the size of something ...
There are multiple problems in your code:
you do not include <stdio.h>.
fgets() is given a very short buffer: 4 bytes, allowing for only 3 characters to be input at a time, including the '\n'. If you type more characters, they are buffered by the terminal and the standard stream library. It will take several calls to fgets() to read them all, 3 bytes at a time.
Your termination test is bogus: strncmp(str, "q\n", sizeof("q\n")) compares the string read by fgets() with "q\n" upto a maximum number of characters of 3 because sizeof("q\n") counts the q, the \n and the null terminator. You should just use strcmp() for this test.
You print the string with printf("\n %s \n", str);. Note however that a regular line read into str will contain the trailing newline so the printf call will actually output 2 lines.
Here is a modified version:
#include <stdio.h>
#include <string.h>
int main(void) {
char str[80];
while (fgets(str, sizeof(str), stdin) != NULL) {
str[strcspn(str, "\n")] = '\0'; // strip the newline if present
printf("\n %s \n", str);
if (!strcmp(str, "q"));
break;
}
return 0;
}
Try using getc() or fgetc() before using fgets()
When you use a scanf(), you press enter key (newline) which operates as accepting the input and transferring the input from stdin (standard input device) to your program.
scanf() itself does not consume the newline pressed. So, we need something down the code which will accept this newline and prevent this newline from acting as an input to the subsequent fgets(). This newline can be accepted using getc() or fgetc(), which should be written before fgets().
fgetc(stdin); OR getc(stdin);

Input taking spaces by default in c

In the below program when am reading input from keyboard its taking only 2 characters instead of 4 and remaining 2 characters its taking spaces by default.
why is it???
program to take char input through pointers/
int c,inc,arrysize;
char *revstring;
printf("enter the size of char arry:");
scanf("%d",&arrysize);
revstring = (char *)malloc(arrysize * sizeof(*revstring));
printf("%d",sizeof(revstring));
printf("enter the array elements:");
for(inc=0;inc<arrysize;inc++)
{
scanf("%c",&revstring[inc]);
}
for(inc =0;inc<arrysize;inc++)
printf("%c",revstring[inc]);
getch();
return 0;
}
scanf reads formatted inputs. When you tape a number, you tape the digits, and then, you press <Enter>. So there is a remaining \n in stdin, which is read in the next scanf. The same applies if you press <Enter> between the characters.
A solution is to consume the characters in the standard input stream after each input, as follow:
#include <stdio.h>
void
clean_stdin (void)
{
int c;
while ((c = getchar ()) != '\n' && c != EOF)
;
}
Another idea is to use fgets to get human inputs. scanf is not suitable for such readings.
Most of the time scanf reads formatted input. For most % formats, scanf will first read and discard any whitespace and then parse the item specified. So with scanf("%d", ... it will accept inputs with initial spaces (or even extra newlines!) with no problems.
One of the exceptions, however, is %c. With %c, scanf reads the very next character, whatever it may be. If that next character is a space or newline, that is what you get.
Depending on what exactly you want, you may be able to just use a blank space in your format string:
scanf(" %c",&revstring[inc]);
The space causes scanf to skip any whitespace in the input, giving you the next non-whitespace character read. However, this will make it impossible to enter a string with spaces in it (the spaces will be ignored). Alternately, you could do scanf(" "); before the loop to skip whitespace once, or scanf("%*[^\n]"); scanf("%*c"); to skip everything up to the next newline, and then skip the newline.

Resources