Reading a number from stdin

Discussion in 'C Programming' started by pandit, May 21, 2014.

  1. pandit

    pandit Guest

    AIM: To read a number from standard input

    WHAT I DID: I use getchar() to read a single digit. Can use strtol() for numbers more than one digit long.

    PROBLEM: isn't there a better way ?

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


    int main(void)
    {
    int i = 0;

    i = getchar();
    if((EOF == i) && (ferror(stdin)))
    {
    printf("ERROR reading input @LINE = %d\n", __LINE__);
    }
    else
    {
    i = i - '0';
    }

    return 0;
    }


    -- output here --
    ../a.out
    1

    output
    1
     
    pandit, May 21, 2014
    #1
    1. Advertisements

  2. pandit

    Stefan Ram Guest

    What you have written, reads a single digit.

    To read a whole numeral, one can use »scanf«.
     
    Stefan Ram, May 21, 2014
    #2
    1. Advertisements

  3. pandit

    Ike Naar Guest

    The program that you have shown does not produce any output.
     
    Ike Naar, May 21, 2014
    #3
  4. Yes and no.
    For most purposes, grabbing a line from stdin then parsing it with sscanf() is fine.
    But there are issues like what to do with an integer which is too big for machine precision,
    which mean that a nice fully general solution is hard, and C doesn't provide one,
    though you can build something on top of strtol().
     
    Malcolm McLean, May 21, 2014
    #4
  5. For simple programs, use scanf. When you need to control the layout
    that you accept, or when you must correctly handle boundary cases like
    digit strings that do not correspond to valid numbers, you need to read
    the data into a buffer and process it with functions like strtol().
    This includes a redundant test and is, almost certainly, not what you
    intended to write. When there has been an error reading a character
    (i.e. when ferror(stdin) is non-zero), i is guaranteed to be EOF.
    Testing both is pointless. You may have intended to test for either
    EOF or and error:

    if (i == EOF || ferror(stdin)) ...
    It's a good idea to get into the habit if writing error messages to
    stderr.
    Your program can't produce this output. Always cut-and-pasts (or
    similar) when posting programs. That avoids people commenting on code
    that is not the code you are actually running!
     
    Ben Bacarisse, May 21, 2014
    #5
  6. I ususally use fgets directed to stdin. That way there's a buffer and
    you don't have to worry about buffer overflow like can happen with scanf.

    Bill
     
    Bill Cunningham, May 21, 2014
    #6
  7. scanf("%d", &i) has undefined behavior if the input matches the
    syntax of an optionally signed integer constant but has a value
    outside the range INT_MIN..INT_MAX.

    (I personally consider this to be a flaw in the language definition.
    The standard explicitly states that it's UB, so it's not
    inconsistent, but it's bad design.)

    You can avoid that by restricting the number of digits, but then
    you can't read certain large values.

    If you want robust behavior, use fgets() (plus whatever you
    need to do to allow for very long lines) followed by one of the
    strto*() functions. (I might write a wrapper around strtol()
    that more cleanly differentiates between the value read and an
    error indication.)
     
    Keith Thompson, May 21, 2014
    #7
  8. Subtracting '0' from i makes no sense unless you already know i is a
    digit (i.e., a value in the range '0' .. '9'). You have no handling for
    invalid input. What happens if the first input character is 'z', for
    example?
     
    Keith Thompson, May 21, 2014
    #8
  9. pandit

    jay Guest


    Why this advice ? I think C-FAQs tell about enough of the issues related with scanf.

    http://c-faq.com/stdio/scanfprobs.html

    footnotes explanation by "your truly" give much better case against scanf.

    strtol() is what I always use myself.



    I think OP used it in correct way:

    Section 15.6, H&s5, (regarding getchar())

    "If an error occurs or if the stream is at end of file, then fgetc returns EOF. The feof and/or ferror facilities should be used in this case to determine whether end of file has really been reached."

    Section 15.1, H&S5 (regarding EOF):

    "Because EOF is sometimes used to signal other problems, it is best to use feof() facility to determine whether end of file has indeed been encountered when EOF is returned."


    -- Arnuld
     
    jay, May 22, 2014
    #9
  10. pandit

    James Kuyper Guest

    There's nothing incorrect about the if() condition, it's simply mildly
    wasteful: it is inherently equivalent to if(ferror(stdin)). The only
    point in including the EOF==i part is to minimize the overhead
    associated with calling ferror(). On systems where ferror(stdin) is
    actually a macro that expands to something like stdin->_eof, that
    overhead can be quite trivial.
     
    James Kuyper, May 22, 2014
    #10
  11. It's good to know the issues, but if the advice it simply not to use
    scanf, ever, then I disagree. If you are using C for some simple
    numerical task, I think scanf is fine. You'll see I went on the explain
    the main reasons to avoid it -- when you worry about overflow and where
    line endings are significant.
    No, it explains the problems that you might encounter mixing scanf and
    gets (yes, gets!). It is not about using scanf on its own. In
    particular, the summery says:

    "The 'better way', as indicated in the FAQ list, is either to abandon
    scanf entirely, or to use it exclusively."

    Not much of a case against scanf. [By the way, it describes using gets
    with a dummy buffer to read an discard up to the end of a line as "ugly,
    unclean" and "unsatisfying". It omits to say "horribly unsafe". The
    page dates, I think, from a more innocent era.]
    You are careful about input. That's good. If, on day one of Numerical
    Analysis 101, you were asked to write something like this:

    int main(void)
    {
    int n = 0;
    double sum = 0, sum_sq = 0, x;
    while (scanf("%lf", &x) == 1) {
    n += 1;
    sum += x;
    sum_sq += x*x;
    }
    if (n)
    printf("N=%d, mean=%g, var=%g\n",
    n, sum/n, (n*sum_sq - sum*sum)/(n*n));
    }

    How would you do it, and would it be worth the effort?
    Note that I did not say it was incorrect, but presumably you are saying
    that I was wrong to say that it includes a redundant test.
    I don't see anything there that contradicts what I said. Why do you
    think it does?
     
    Ben Bacarisse, May 22, 2014
    #11
  12. pandit

    jay Guest

    I see your reasoning but still I have two mental-blocks in recommending scanf():

    1) scanf() means scan formatted and interactive-terminal is the least formatted thing you can ever across.

    2) What about Chris Torek's footnote. It simply says, there is a right answer to read user input you should either use fegts() with strtol() or fgets() with sscanf().


    My bigger problem is looking at programmers in Indian companies actually. Their ways of working frustrates me sometimes. Every programmer I have met in industry here uses scanf() 100% of the time and that too without knowing about it how it works. We ran into some problems because someone in team used scanf() and all hell broke loose because he did not know scanf() leaves the input on stream, it is exactly like the input "three" in Chris Torek's footnote. Now when I try to explain how scanf() works and what caused the issue, then no one even believes me. Even if I point them to CLC or n1570.pdf , it does not matter, no one listens and 100% of the people have hated me for telling them about scanf() works. Of all the people I have met in last 7 years, not one has ever read the standard and they detest it.

    So, I have developed hatred for scanf() family. That is my mental block. But I want to learn C better and better everyday and become expert like Keith Thompson and that is the reason I always keep on coming back to CLC andthat is why I posted my mental block because I think it may prevent me from becoming better with C


    Literally no idea what meaningful work this program does

    yes, that is what I am saying. See down below.




    Read this:

    "Because EOF is sometimes used to signal other problems, it is best to
    use feof() facility to determine whether end of file has indeed been
    encountered when EOF is returned."


    It say, when EOF is returned then you shoudl use feof() facility to check for errors. It does *not* say that you should use either EOF or feof(). It says when EOF is returned then use feof().

    Same thing I am doing, I am checking that EOF is returned and only then (&&) I am checking ferror() (because I don't know the difference between usingfeof() and ferror())
     
    jay, May 23, 2014
    #12
  13. fgets() with sscanf() is still perilous for numeric input. All the
    *scanf() functions (scanf, fscanf, sscanf) have undefined behavior when
    scanning numeric input if the input is syntactically correct but outside
    the range of the target type. (You can use, for example, "%5d" to limit
    the number of digits, but then there will be valid numbers you can't
    read.)

    At least with sscanf() you can in principle avoid feeding it a string
    with an overflowing number -- but if you can determine that, then you've
    probably already determined the value anyway.
    It reads a sequence of floating-point numbers from standard input,
    keeping track of who many numbers have been read, the sum of the
    numbers, and the sum of the squares of the numbers. It then computes
    and prints the mean and variance.

    [...]
    Not quite. The point is that this:

    i = getchar();
    if((EOF == i) && (ferror(stdin)))

    is logically equivalent to this:

    i = getchar();
    if (ferror(stdin))

    After getchar() returns EOF exactly one of feof() and ferror() will be
    true.

    If you just want to read as many characters as you can, you don't need
    to call ferror() or feof() at all; you can just keep reading until
    getchar() returns EOF. If you want to distinguish between an
    end-of-file condition and an error condition, you can do something like
    this:

    int c;
    while ((c = getchar()) != EOF) {
    /* do something with c, which is a character read from stdin */
    }
    /* Now we've run out of input -- but why? */
    if (ferror(stdin)) {
    printf("Error reading from stdin\n"):
    }
    else {
    printf("No error, just ran out of input\n");
    }

    If you don't know the difference between feof() and ferror(), I suggest
    you find out.
     
    Keith Thompson, May 23, 2014
    #13
  14. Normally the valid numbers for the application are much lower than INT_MAX,
    so the %5d method works for most situations.
    I checked my version of sscanf, and it sets the target to 0xFFFFFFFFF (all bits set)
    and returns 1 on begin fed a massive integer. That's undesirable, because it
    should be returning 0 and setting the stream to the start position. Sometimes
    you just want to skip a number and parse the following text, in which case
    my compiler's behaviour is best, but if the value is used, the program will get
    into an odd state, even if checks are made.
     
    Malcolm McLean, May 23, 2014
    #14
  15. pandit

    jay Guest

    Because of this reason I always check strtol() for errors. IIRC, you posted about the errors scenarios few years back as a reply to one of my posts.



    My math is a bit weak, don't know the difference between "mean", "variance", "average" and "median". I will work on it.


    What if I am reading input only once and there is an error in reading it ? Can't I use ferror() in this case ?




    ok, I get it.

    -- feof() : checks if end of file has been detected while reading from the input stream. It signals end of file when an attempt is made to read *past* the last character. If it was really an end of file then it returns non-zero value else zero.

    -- ferror() : returns error status of stream. if an error has occurred while reading from or writing to the stream, then it returns non-zero value else zero.

    stream -- any of 4: pointer to a file/stdin/stdout/stderr.
     
    jay, May 23, 2014
    #15
  16. "mean" is the total divided by the number. It's also what we commonly mean by the "average",
    though that's a loose term which doesn't necessarily have a mathematical meaning. (An average
    sort of football player).
    median is the point at which half the population is above and half below the value. Median
    is often more useful than mean for describing things like salaries, where the mean can be
    skewed by a few people on extremely high salaries, giving a flattering impression of the
    typical salary.

    Variance is a measure of spread. Most adult men are between 6 foot two and five foot tall,
    with only a few outside these limits. When you include women and children, you get a
    huge range, with fairly large numbers of people under three foot tall (little children).
    So variance amongst adult males is quite low, amongst humans in general, pretty high.
     
    Malcolm McLean, May 23, 2014
    #16
  17. pandit

    James Kuyper Guest

    Well, the key point of the question was not the math (and all of the
    relevant formulas were already present in the code). The key point was,
    if you're not going to use scanf(), what alternative would you use.
    fgets()/strtol() is a good answer to that question - but that approach
    is significantly more complicated than using scanf().
    You can always use ferror(stream) to determine whether or not there has
    been an I/O error on that stream. It doesn't matter how many times
    you've read input from that stream. It even works if you've never done
    so. The key issue is that it's generally not useful to continue reading
    from a given stream after having had an I/O error, and it's not
    necessary to check for either feof() or ferror() until after getchar()
    has returned EOF. It's perfectly safe, just not useful.
     
    James Kuyper, May 23, 2014
    #17
  18. Keith has provided all the technical answers already, but since there is
    some opinion here, I'll do some duplication and answer myself...
    That's an argument from a name. The formatted refers to the format
    string, not to the structure of the input. In fact, the main problem
    with scanf for interactive input is that scanf can't impose a format on
    the input, at least not in the sense that other languages like Fortran
    can; %d, %f and so on denote what other languages would call
    "free-form" input.
    He is welcome to his opinion. I disagree. Reading lines if very often
    the right thing to do, but blanket rules are not a good idea in
    programming. In fact, while you are learning programming your should
    try to think of an exception to every rule you hear. You may not find
    one, but the trying is worth it.
    All input operations 9except for the last one) leave input on the
    stream. Whatever the problem was, it was not that!
    OK, but with that level of denial, are you sure that using fgets +
    sscanf (both of which are tricky to use correctly) would help?
    Well, try the exercise I suggested then:
    Do you work programming in C?
    Well, I stick to what I said. Keith, who you quite correctly regard as
    an expert, has explained some more about this.
    You've not really understood what the passage you quote is saying but
    I'll leave this for now since it's been addressed and I see further
    posts in the thread. If you remain unconvinced, feel free to ask again
    later.
     
    Ben Bacarisse, May 23, 2014
    #18
  19. That's very risky advice. Try scanf("%d %d", &i, &j) and feed it
    -10001. How surprised will the use be?

    Also, %5d is not small enough to be safe with 16-bit ints. These may be
    rare, but you never know when they will make a come-back.

    <snip>
     
    Ben Bacarisse, May 23, 2014
    #19
  20. (something has gone wrong with the quoting level, but the names are right)
    The maths does not matter. Chuck out everything but finding the sum
    (you know that much maths, I'm sure). The point of the exercise was to
    see how you write the input using your better safe methods; and,
    ultimately, to see if it worth it in this case.

    <snip>
     
    Ben Bacarisse, May 23, 2014
    #20
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.