1

Is there a library function to read a line of input from stdin with the following requirements?

  1. I have a limited static buffer of specific size (size may be a known constant).
  2. No dynamic allocation allowed. So the library functions like getline() cannot be used.
  3. For lines whose length is beyond the buffer size, the unread tail part of the line is to be ignored.

My solution to read a line is using fgets and a loop to read and ignore the tail part. The code is as below

char buffer[80], tail_buffer[80];
char * line, * tail;
line = tail = fgets(buffer, 80, stdin);
/* Read the tail part to ignore it */
while (tail != NULL && tail[strlen(tail)-1] != '\n')
{
    tail = fgets(tail_buffer, 80, stdin);
}
/* Use 'line' as needed */
user1969104
  • 2,340
  • 14
  • 15
  • I wouldn't say your solution is really elegant for a few reasons, one of them being that you're using `while(!feof(stdin))`, which is [wrong](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – Linus Nov 02 '15 at 19:20
  • 2
    Try posting this on http://codereview.stackexchange.com/ – jayant Nov 02 '15 at 19:21
  • @Linus What is wrong with !feof(stdin)? I use this because the end character may not be '\n' on EOF. But this may be unnecessary, as the next invocation of fgets will anyway return NULL. – user1969104 Nov 02 '15 at 19:25
  • @jayant No code review needed here. I just need to understand if there is a library function similar to getline() that reads a complete line into a limited buffer by truncating the tail part. – user1969104 Nov 02 '15 at 19:38
  • "I am trying to find a better solution". That's for codereview. – Weather Vane Nov 02 '15 at 19:46
  • @WeatherVane I rephrased my question. Does this still sound like a need for code-review? – user1969104 Nov 02 '15 at 19:53

3 Answers3

1

After looking into the documentation of scanf, I found a solution myself.

char buffer[80];
scanf(" %79[^\n]%*[^\n]", buffer);

EDIT: With the comments from @chux that this has some limitations in parsing the blank lines and initial spaces, and with the solution from @user3386109, I enhance this as below to scan all the lines till EOF.

char buffer[80] = "";

while (scanf("%79[^\n]%*[^\n]", buffer) != EOF)
{
    /* Process the line in buffer */

    if (feof(stdin)) break;
    getchar(); /* Remove end of line */
    buffer[0] = 0;
}
user1969104
  • 2,340
  • 14
  • 15
  • `%80` should be `%79`. also The beginning of the space is discarded. – BLUEPIXY Nov 02 '15 at 19:55
  • @chux Thanks, I understand the limitation. But for now, I can assume that blank lines are not needed. – user1969104 Nov 02 '15 at 20:01
  • @BLUEPIXY, I do not understand what you mean. Please elaborate. – user1969104 Nov 02 '15 at 20:02
  • @BLUEPIXY, That's the reason why I added a space in front for format string. Any suggestions to improve? – user1969104 Nov 02 '15 at 20:09
  • 1
    `" %79[^\n]..."` will ignore leading white-space in a line, so input like `"\t123"` ignores the leading `'\t'`. Ignoring leading white-space in not mentioned as allowable in the post. – chux - Reinstate Monica Nov 02 '15 at 20:12
  • @chux That's true. This solution isn't good in handling initial space or blank lines. – user1969104 Nov 02 '15 at 20:15
  • From [comment](http://stackoverflow.com/questions/33485153/read-size-limited-input-line-ignoring-the-tail-part/33485671?noredirect=1#comment54756515_33485671), there is nothing in the post to suggest blank lines are not needed. – chux - Reinstate Monica Nov 02 '15 at 20:15
  • @chux I agree. I do not still accept this as the correct solution according to the problem statement. – user1969104 Nov 02 '15 at 20:20
  • 1
    I think probably operation is desired by one of scanf (or one sentence) that's impossible. – BLUEPIXY Nov 02 '15 at 20:25
  • 1
    With the new `char buffer[80]; scanf("%79[^\n]%*[^\n]", buffer), getchar()`, should the first `char` be `'\n'`, nothing is save in `buffer` and likely will cause a problem when subsequent code uses an uninitialized buffer. Robust code does not use `scanf()`. – chux - Reinstate Monica Nov 02 '15 at 20:48
  • @chux :-). I try to initialize buffer before the scanf. – user1969104 Nov 02 '15 at 20:51
  • @chux If I drop `&&` and press Ctrl-D at the beginning of line, then getchar() is waiting for an additional Ctrl-D. There are still some issues with EOF after entering partial line and pressing Ctrl-D, that I may need to fix. – user1969104 Nov 02 '15 at 21:00
  • 1
    The latest looks OK - except for 1 tiny hole - should you care - it is obscure. Code does not use the reason for `scanf()` return. `scanf()` returns `EOF` under 2 conditions: end-of-file and input error. With input error, the value of `buffer` is indeterminate. So any attempt to pre-load `buffer` with a value like `buffer[0]=0` is moot when `EOF` is returned and code does not check `feof()` / `ferror()`. IOWs do not use `buffer` when an input function returns `EOF`. – chux - Reinstate Monica Nov 02 '15 at 21:08
  • @chux, Thanks, I understand. I think it is easy to ensure that after EOF from scanf, buffer is not processed. I will edit it accordingly. – user1969104 Nov 02 '15 at 21:20
1

fgets() has corner cases that preclude using it at a complete solution to OP's goal.
Simply loop using fgetc().

// Return count of `char` read - not including potential \n.
int read_line(char *dest, int size) {
  int i = 0;
  if (size > 0) {
    size--;
    int ch;
    while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
      if (i < size) {
        dest[i++] = ch;
      }
    }
    dest[i] = '\0';
    if (ch == EOF && i == 0) return EOF;
    }
  return i;
}

A forced use of fgets() looks like

bool was_there_extra(char *buf, size_t size) {
  char *lf = strchr(buf, '\n');
  if (lf) {
    *lf = '\0';  // optional: lop off potential trailing \n
    return false;
  }
  int ch;
  bool extra = false;
  while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
    extra = true;
  }
  return extra;
}

while (fgets(buf, sizeof buf, stdin)) {
  if (was_there_extra(buf, sizeof buf)) ...
  else ...
}

This approach does get fooled if code reads a '\0'.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

An alternative solution using scanf to read until the newline is found, and the getchar to read the newline.

char buffer[80];
if ( fgets( buffer, sizeof buffer, stdin ) != NULL )
{
    if ( strchr( buffer, '\n' ) == NULL )   // if the buffer does not contain the newline
    {
        scanf( "%*[^\n]" );                 // read up to the newline
        getchar();                          // read the newline
    }
}
user3386109
  • 34,287
  • 7
  • 49
  • 68
  • That's sounds like a feasible solution. May be you can try using only scanf and getchar and not mixing it with fgets, like as in `scanf("%79[^\n]%*[^\n]", buffer); getchar();` – user1969104 Nov 02 '15 at 20:28
  • 1
    Corner case: `strchr( buffer, '\n' )` only reads `buffer` to the `'\0'` which may be before the `'\n'` if `fgets()` read a null character. Unusual to find a null character in user input, but it is a hacker's crack. – chux - Reinstate Monica Nov 02 '15 at 20:37
  • I think there is no need to check for '\n' to determine whether to read tail or not, if we read buffer through scanf itself. This is because, even for the case when there is no tail (everything read into buffer), `%*[^\n]` will just parse empty. – user1969104 Nov 02 '15 at 20:41
  • @chux You are correct that having a null character in the input can cause a line to be skipped, but has no other ill effects. – user3386109 Nov 02 '15 at 20:41