C - Program exits when input exceeds fgets allowance - c

I have the following program written in C:
The main problem with this program is that if the input exceeds 80 characters when using fgets() function, the program just exits immediately. The other code is executed, however it does not wait for the user to press enter. It like simply ignores the getchar at the end.
How can I solve this problem please?

If the user input is longer than the 79 characters that fgets may read from stdin (it can read at most one less than its size parameter says, since it 0-terminates the buffer), the remaining input is left in the input buffer, hence the getchar() at the end immediately succeeds.
To avoid that, you need to clear the input buffer if the input was too long.
The problem is that if the input was short enough, you don't know whether to clear the buffer or not. So check whether you actually got a newline read in by fgets,
int len = strlen(password);
if (password[len-1] == '\n') {
// got a newline, all input read, overwrite newline
password[len-1] = 0;
} else {
// no newline, input too long, clear buffer
int ch;
while ((ch = getchar()) != EOF && ch != '\n');
if (ch == EOF) {
// input error, stdin closed or corrupted, what now?
}
}

Check if a new-line character was read by fgets(), and if not skip input until a new-line character is encountered:
if (0 == strrchr(password, '\n'))
{
/* Skip until new-line. */
int c;
while (EOF != (c = getchar()) && '\n' != c);
}
otherwise the call to getchar() will read what fgets() did not.

Related

Usage of scanf ... getchar

Is the following pattern ok in C to get a string up until a newline?
int n = scanf("%40[^\n]s", title);
getchar();
It seems to work in being a quick way to strip off the trailing newline, but I'm wondering if there are shortcomings I'm not seeing here.
The posted code has multiple problems:
the s in the format string is not what you think it is: the specification is %40[^\n] and the s will try and match an s in the input stream, which may occur after 40 bytes have been stored into title.
scanf() will fail to convert anything of the pending input is a newline, leaving title unchanged and potentially uninitialized
getchar() will not necessarily read the newline: if more than 40 characters are present on the line, it will just read the next character.
If you want to read a line, up to 40 bytes and ignore the rest of the line up to and including the newline, use this:
char title[41];
*title = '\0';
if (scanf("%40[^\n]", title) == EOF) {
// end of file reached before reading anything, handle this case
} else {
scanf("%*[^\n]"); // discard the rest of the line, if any
getchar(); // discard the newline if any (or use scanf("%1*[\n]"))
}
It might be more readable to write:
char title[41];
int c, len = 0;
while ((c = getchar()) != EOF && c != '\n') {
if (len < 40)
title[len++] = c;
}
title[len] = '\0';
if (c == EOF && len == 0) {
// end of file reached before reading a line
} else {
// possibly empty line of length len was read in title
}
You can also use fgets():
char title[41];
if (fgets(title, sizeof title, stdin) {
char *p = strchr(title, '\n');
if (p != NULL) {
// strip the newline
*p = '\0';
} else {
// no newline found: discard reamining characters and the newline if any
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
}
} else {
// at end of file: nothing was read in the title array
}
Previous note, the s should be removed, it's not part of the specifier and is enough to mess up your read, scanf will try to match an s character against the string you input past the 40 characters, until it finds one the execution will not advance.
To answer your question using a single getchar is not the best approach, you can use this common routine to clear the buffer:
int n = scanf(" %40[^\n]", title);
int c;
while((c = getchar()) != '\n' && c != EOF){}
if(c == EOF){
// in the rare cases this can happen, it may be unrecoverable
// it's best to just abort
return EXIT_FAILURE;
}
//...
Why is this useful? It reads and discards all the characters remaing in the stdin buffer, regardless of what they are.
In a situation when an inputed string has, let's say 45 characters, this approach will clear the stdin buffer whereas a single getchar only clears 1 character.
Note that I added a space before the specifier, this is useful because it discards all white spaces before the first parseable character is found, newlines, spaces, tabs, etc. This is usually the desired behavior, for instance, if you hit Enter, or space Enter it will discard those and keep waiting for the input, but if you want to parse empty lines you should remove it, or alternatively use fgets.
There are a number of problems with your code like n never being used and wrong specifier for scanf.
The better approach is to use fgets. fgets will also read the newline character (if present before the buffer is full) but it's easy to remove.
See Removing trailing newline character from fgets() input

Don't know how to use getchar function correctly / C

I wanted to create a function that inputs a character in the console but the problem is that sometimes when I input 'a' it is considered as an empty char and the programm asks me to re-input a char.
This is the function :
char readChar()
{
char character;
character = getchar();
character = toupper(character);
while(getchar() != '\n' && getchar() != '\0');
return character;
}
Converting a barrage of comments into an answer.
1. Note that getchar() returns an int, not a char. And your loop needs to take into account EOF more than a null byte, though it's probably OK to detect null bytes.
My best guess about the trouble is that sometimes you do scanf("%d", &i) or something similar, and then call this function — but scanf() doesn't read the newline, so your function reads a newline left over by previous I/O operations. But without an MCVE (Minimal, Complete, and Verifiable Example), we can't demonstrate that my hypothesis is accurate.
2. Also, your 'eat the rest of the line' loop should only call getchar() once on each iteration; you call it twice. One option would be to use:
int readChar(void)
{
int c;
while ((c = getchar()) != EOF && isspace(c))
;
if (c == EOF)
return EOF;
int junk;
while ((junk = getchar()) != '\n' && junk != EOF /* && junk != '\0' */)
;
return toupper(c);
}
This eats white space until it gets a non-white-space character, and then reads any junk characters up to the next newline. It would fix my hypothetical scenario. Beware EOF — take EOF into account always.
Based on reading the Q&A about scanf() not reading a newline, Voltini proposed a fix:
char readChar()
{
char character;
scanf(" %c", &character);
getchar(); //I just added this line
character = toupper(character);
return character;
}
3. That is often a good way to work. Note that it has still not dealt with EOF — you always have to worry about EOF. The getchar() after the scanf() will read the newline if the user typed a and newline, but not if they typed a-z and then newline. You have to decide what you want done with that – and a character gobbling loop is often a good idea instead of the single getchar() call:
int c;
while ((c = getchar()) != EOF && c != '\n')
;
And in response to a comment along the lines of:
Please explain the importance of handling EOF.
4. If you don't ask, you won't necessarily learn about it! Input and output (I/O) and especially input, is fraught. Users don't type what you told them to type; they add spaces before or after what you told them to type; you expect something short like good and they type supercalifragilisticexpialidocious. And sometimes things go wrong and there is no more data available to be read — the state known as EOF or "end of file".
5. In the function with char character; scanf(" %c", &character); and no check, if there is no input (the user types ^D on Unix or ^Z on Windows, or the data file ended), you have no idea what value is going to be in the variable — it is quasi-random (indeterminate), and using it invokes undefined behaviour. That's bad. Further, in the code from the question, you have this loop, which would never end if the user indicates EOF.
while (getchar() != '\n' && getchar() != '\0') // Should handle EOF!
;
6. And, to add to the complexity, if plain char is an unsigned type, assigning to character and testing for EOF will always fail, and if plain char is a signed type, you will detect EOF on a valid character (often ÿ — small latin letter y with diaeresis in Unicode and 8859-1 or 8859-15 code sets). That's why my code uses int c; for character input. So, as you can see (I hope), there are solid reasons why you have to pay attention to EOF at all times. It can occur when you don't expect it, but your code shouldn't go into an infinite loop because of that.
I'm not sure how and where to … implement this … in my code.
7. There are two parts to that. One is in the readChar() function, which needs to return an int and not a char (for the same reasons that getchar() returns an int and not a char), or which needs an alternative interface such as:
bool readChar(char *cp)
{
int c;
while ((c = getchar()) != EOF && isspace(c))
;
if (c == EOF)
return false;
*cp = toupper(c);
while ((c = getchar()) != '\n' && c != EOF /* && c != '\0' */)
;
return true;
}
so that you can call:
if (readChar(&character))
{
…process valid input…
}
else
{
…EOF or other major problems — abandon hope all ye who enter here…
}
With the function correctly detecting EOF, you then have your calling code to fix so that it handles an EOF (error indication) from readChar().
Note that empty loop bodies are indicated by a semicolon indented on a line on its own. This is the way K&R (Kernighan and Ritchie in The C Programming Language — 1988) wrote loops with empty bodies, so you find it widely used.
You will find over time that an awful lot of the code you write in C is for error handling.
Do not read potentially twice per loop as with while(getchar() != '\n' && getchar() != '\0'); #Jonathan Leffler
To read a single and first character from a line of user input:
A text stream is an ordered sequence of characters composed into lines, each line
consisting of zero or more characters plus a terminating new-line character. Whether the
last line requires a terminating new-line character is implementation-defined. C11dr §7.21.2 2
Use getchar(). It returns an int in the range of unsigned char or EOF.
Read the rest of the line.
Sample code, a bit like OP's.
int readChar_and_make_uppercase() {
int ch = getchar();
if (ch != '\n' && ch != EOF) {
// read rest of line and throw it away
int dummy;
while ((dummy = getchar()) != '\n' && dummy != EOF) {
;
}
}
return toupper(ch);
}
Apparently, I forgot to take care of the newline character stored in the buffer (correct me if I'm wrong).
char readChar()
{
char character;
scanf(" %c", &character);
getchar(); //I just added this line
character = toupper(character);
return character;
}

fgets from stdin with unpredictable input size

I'm trying to read a line from stdin but I don't know to properly handle the cases when input size is at least equal to the limit. Example code:
void myfun() {
char buf[5];
char somethingElse;
printf("\nInsert string (max 4 characters): ");
fgets (buf, 5, stdin);
...
printf("\nInsert char: ");
somethingElse = getchar();
}
Now, the user can do three things:
Input less than 4 characters (plus newline): in this case there's nothing left in stdin and the subsequent getchar() correctly waits for user input;
Input exactly 4 characters (plus newline): in this case there's a newline left in stdin and the subsequent getchar() reads it;
Input more than 4 characters (plus newline): in this case there's at least another character left in stdin and the subsequent getchar() reads it, leaving at least a newline in.
Cases 2 and 3 would require emptying stdin using something like while(getchar() != '\n'), whereas case 1 doesn't require any additional action. As I understand from reading answers to similar questions and c-faq, there's no standard/portable way to know whether the actual scenario is the one described in 1 or not.
Did I get it well? Or there actually is a portable way to do it? Or maybe a totally different approach?
The fgets function will store the newline in the buffer if there is room for it. So if the last character in the string is not a newline, you know you need to flush the buffer.
fgets (buf, 5, stdin);
if (strrchr(buf, '\n') == NULL) {
// flush buffer
int c;
while ((c = getchar()) != '\n') && (c != EOF));
}
If ones assumes that a null character '\0' is never read, then #dbush answer will work.
If a null character is read, then strrchr(buf, '\n') does not find any '\n' that may have been read.
Code could pre-set the buffer to see if a '\n' was read in the end.
buf[sizeof buf - 2] = '\n';
if (fgets (buf, sizeof buf, stdin)) {
if (strrchr(buf, '\n') == NULL) {
// incomplete line read. (the usual detection)
} else if (buf[sizeof buf - 2] != '\n') {
// incomplete line read with prior null character (see below note).
}
}
Yet the C standard does not specify that data past what was read in buf[] is unchanged, pre-filling the buffer with some pattern is not sufficient to detect if a null character '\0' was read.
is a portable way to do it?
The most portable way is to use repeated calls to fgetc() or the like instead of fgets().
maybe a totally different approach?
I recommend fgetc() or the common but not C standard getline()
Another alternative: Use scanf("%4[^\n]%n", buf, &n): It is very cumbersome, yet a portable way is possible. It keeps track of the number of characters read before the '\n' even if some are null characters.
int n = 0;
cnt = scanf("%4[^\n]%n", buf, &n); // Cumbersome to get that 4 here.
// Lots of TBD code now needed:
// Handle if cnt != 1 (\n to be read or EOF condition)
// Handle if n == sizeof buf - 1, (0 or more to read)
// Handle if n < sizeof buf - 1, (done, now get \n)
// A \n may still need to be consumed
// Handle EOF conditions

Clear input stream after fgets set null char before newline

I have a program where the user enters two inputs. Being that I can't control what the user enters, the user can go past the fixed size of the array. Since fgets() appends retains a newline to the end before the null character, in the event that a newline cannot fit when the user goes beyond the intended size, the null character truncates the input. Does the newline character when the user hits enter still exist in the input stream? If so, is this the reason why fgets()skips the second time because of the newline from the first input?
#include <stdio.h>
int main(){
char str[5];
fgets(str,5,stdin);
printf("Output:%s",str);
fgets(str,5,stdin);
printf("Output:%s",str);
return 0;
}
Example Input
ABCDE\n
Output
Output:ABCDOutput:E
After reading this SO answer fgets() isn't prompting user a second time
, the issue seems to be not flushing the input stream via fflush(stdin), but I've heard conflicting information saying as this leads to undefined behavior. My last question is, what would be the appropriate way to clear the input stream if it's the retained newline that's causing issues?
the user can go past the fixed size of the array. Since fgets() appends a newline to the end before the null character
No, it does not. It writes characters read from the input into the provided buffer, up to and including the first newline, or until the specified buffer size is exhausted (less one byte for the string terminator), or until an error occurs or the end of the stream is reached, whichever comes first. The newline is not invented by fgets(); it comes from the input.
, in the event that a newline cannot fit when the user goes beyond the intended size, the null character truncates the input. Does the newline character when the user hits enter still exist in the input stream?
All characters entered by the user and not copied into the buffer remain waiting to be read in the stream. That will include the newline, if the user entered one.
If so, is this the reason why fgets()skips the second time because of the newline from the first input?
fgets() does not skip, but it does pick up where the previous call left off transferring characters from input to the buffer. No characters are lost. That means that the second call returns part of the first input line if the first call did not return the whole thing. You need to account one way or another for the possibility that the input does not conform to your line-length expectations.
the issue seems to be not flushing the input stream via fflush(stdin),
No, it isn't. Flushing is for sending buffered output to the underlying output device. Flushing an input stream produces undefined behavior. In principle, that could manifest as a buffer dump, and a given implementation might even specify such behavior, but you don't want that because there may be more data buffered than you want to get rid of.
but I've heard conflicting information saying as this leads to undefined behavior. My last question is, what would be the appropriate way to clear the input stream if it's the retained newline that's causing issues?
You read from the input until you've read the newline. There are plenty of I/O functions to choose from to accomplish this. fgets() itself might prove convenient, since you're already using it:
char str[5];
if (fgets(str, 5, stdin)) {
printf("Output:%s", str);
// read and consume the tail of the line, if any (overwrites str)
while (!strchr(str, '\n') && fgets(str, 5, stdin)) { /* empty */ }
}
fgets() reads until
1) New-line
2) Buffer is full
3) End-of-file
4) Input error (rare)
This code reads and takes care of #3 & #4
#define N 5
char buf[N];
if (fgets(buf, sizeof buf, stdin) == NULL) {
// Handle EOF or Error
return EOF;
}
To distinguish if a '\n' is present ... (#2 from #1)
// look for a lack of \n
if (strchr(buf, '\n') == NULL) {
And if so, read until it is found or EOF.
int ch;
while ((ch = fgetc(stdin)) != '\n' && ch != EOF);
}
--
Do not use the following code. It can be exploited by reading a null character as the first character.
size_t len = strlen(buf);
if (buf[len - 1] != '\n') { // bad way to detect \n
Could use
if (len > 0 && buf[len - 1] != '\n') { // Good
Being that I can't control what the user enters...
No, you cannot.
the user can go past the fixed size of the array.
Right. This is always a concern. However, In general you'll want to arrange things so that this rarely happens.
For example, if you really want to limit the user to (say) a 4-character-long input string, let him type whatever he wants, then see how much he typed, and if it was more than your limit, print a nice error message or something. But I do not recommend calling fgets(str, 5, stdin) if you're expecting 4 characters of input plus a newline, because it's just way too hard to recover when (not if) the user types too much.
in the event that a newline cannot fit when the user goes beyond the intended size, the null character truncates the input. Does the newline character when the user hits enter still exist in the input stream?
Absolutely yes.
If so, is this the reason why fgets()skips the second time because of the newline from the first input?
Pretty much yes.
I recommend allocating a much bigger buffer, and then proceeding something like this:
char inpbuf[512);
if(fgets(inpbuf, sizeof(inpbuf), stdin) == NULL) {
fprintf(stderr, "end of file\n");
return;
}
char *p = strrchr(inpbuf, '\n');
if(p == NULL) {
fprintf(stderr, "looks like you typed *way* too much\n");
return;
}
*p = '\0'; /* erase the \n */
if(strlen(inpbuf) > 4) {
fprintf(stderr, "you typed too much (max 4)\n");
return;
}
strcpy(str, inpbuf);
printf("Output:%s", str);
One glitch with this code as written, though: if the user hits the end-of-file key (control-D on Unix/Linux) before hitting Return, you'll falsely get the "looks like you typed way too much" message.
If the string read by fgets doesn't end in a newline, you know it's still in the buffer. In that case, call getchar in a loop until you get a newline.
fgets(str,5,stdin);
printf("Output:%s",str);
if (strchr(str, '\n') == NULL) {
int c;
while ((c = getchar()) != EOF && c != '\n');
}
fgets(str,5,stdin);
printf("Output:%s",str);

while (getchar () != '\n' );

I have the following for loop, I am prompting the user to enter a 4 digit pin and hit enter. Can someone explain to me what the while loop is really doing because I don't fully understand it.
//user input for pin
for(i = 0; i < PIN_LENGTH; i++)
{
printf("Enter digit %d of your PIN: ", i);
user_pin[i] = getchar();
while (getchar() != '\n'); //what is this line doing??
}
As mentioned by others, this loop discards unwanted characters from stdin so that the next input function has a clean stream, and in particular it discards the \n that follows the last character entered by the user. But, getchar() returns EOF in the event of a read error, so the loop should test for EOF also, like this:
int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
continue; // discard unwanted characters
Also note that, if stdin has been redirected, it is possible to reach EOF without encountering a \n. And, as #chqrlie pointed out in the comments, the user can signal an EOF from the terminal by entering Ctrl-D in Linux, or Ctrl-Z in Windows. Hence the importance of testing for EOF explicitly.
When you give input to the program, then you end it with the Enter key. This key is sent to your program as a newline.
What the loop does is to read and discard characters until it has read the newline. It flushes the input buffer.
If you don't do this, then the next main getchar call would return the newline instead of the digit you expect.
The next line is discarding the possible extra chars that the user may have inputted, and also the linefeed char that the user had to input.
So other scanf/getchar methods further in the code are not polluted by this spurious input.
The while loop is used to check when the user will press enter.Enter is considered as '\n'.
That line is a while, so at run-time it will repeat it until getchar() == '\n'. '\n' is a character that in ASCII means "enter" key. In this way you can test that the user will not press "enter" without had inserted a number.
**The function of this while loop is to clear the users' illegal input .When the input is '\n' ,the loop ends.

Resources