read ints from standard input until \n is found

Viewed 140

I'm trying to make a function that reads ints from stdin. it has to read until a certain amount of numbers is read (count in example below), or until it finds a '\n'.

Since as far as I am aware scanf (with %d format specifier) ignores newlines, I used getchar and converted the character into the number it should be. this works but only for 1 digit numbers. is there any better way to achieve this?

This is my code:

char num = getchar();
while (num != '\n' && count < 9) {
    //boring operations that don't matter
    num = getchar()
}
4 Answers

Reading via fgets() is better. Continue reading if your must use scanf().


To use scanf("%d",...), we need extra care to read a line. As "%d" consumes leading white-space, including '\n', we need more code to look for white-space and test if a '\n' is found.

int count = 0;
while (count < 9) {
  // Read leading spaces
  int ch;
  while (isspace((c = getchar())) && c != '\n') {
    ;
  }
  if (c == '\n' || c == EOF) break; // We are done reading
  ungetc(c, stdin); // put character back

  int some_int;
  if (scanf("%d", &some_int) == 1) {
    printf("Integer found %d\n", some_int);
    count++;
  } else {
    // Non-numeric input, consume at least 1 character.
    getchar();
  }
}

If numeric text is outside the range of int, the above use of "%d" is undefined behavior. For robust code, use fgets().

Someone posted an answer and then deleted but it was the perfect solution for my problem, so all credit to the original author.

The solution was reading normally with scanf and afterwards,with getchar, checking if it was \n or EOF. If it was break out of the cycle, if it wasn't, "unread" with ungetc so you can scanf the number in the next iteration.

So my final code looks like this:

while(scanf("%d",&num) == 1 && count<9){
    //boring operations
    c = getchar();
    if (c == EOF || c == '\n') break;
    if (ungetc(c,stdin) == EOF) break;
}

NOTE: like Andrew Henle pointed out in the replies, this doesn't work unless it is guaranteed that there isn't any space between the digits and the newline

The %d conversion specifier only ignores leading whitespace. So you can do something like:

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **argv)
{
        int n = argc > 1 ? strtol(argv[1], NULL, 10) : 10;
        int x;

        while( n-- && scanf("%d%*[ \t]", &x) == 1 ){
                printf("Read: %d\n", x);
                int c = getchar();
                if( c == EOF || c == '\n' ){
                        break;
                }
                ungetc(c, stdin);
        }

        return 0;
}

However, this will probably not handle a stream like 10 5 x in a reasonable way. You'll need more logic on the first non-whitespace after an integer to handle that (maybe just do if( c == EOF || ! isdigit(c) ){ break; }). Parsing data with scanf if fickle (it really never has a purpose outside of university exercises). Just use fgets and strtol.

scanf() doesn't ignore \n

#include <stdio.h>
#include <stddef.h>

int main(int argc , char *argv[])
{
    int b;
    char c;
    scanf("%d%c",&b,&c);

    if(c == '\n') printf("and then  " );
}
Related