1

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:

  1. 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;
  2. Input exactly 4 characters (plus newline): in this case there's a newline left in stdin and the subsequent getchar() reads it;
  3. 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?

3
  • 5
    Check to see if buf[] contains a newline? Commented Dec 8, 2017 at 16:56
  • 2
    It's maximum 3 chars, not 4, because the \n goes also into the buffer. Commented Dec 8, 2017 at 16:59
  • You're right @MichaelWalz, the user should be warned about that. Commented Dec 8, 2017 at 17:09

2 Answers 2

4

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)); } 
Sign up to request clarification or add additional context in comments.

1 Comment

That's actually so simple, I feel ashamed that I haven't thought about it.
2

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 

2 Comments

I discarded the "repeated fgetch() option, but I din't think about null characters being input by user. It's not that likely to happen, but you're never too safe, I guess. The fact that the C standard does not guarantee data after the \0 to stay unchanged, actually brings the fgetc() option back to life. Good answer!
@pimple "It's not that likely to happen." I agree - unless that user is a hacker. Much of (early) C is predicated on a trusted user. That model is risky going forward. Code as needed.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.