Thread: Reading ints and doubles using scanf

  1. #1
    Registered User
    Join Date
    Apr 2015
    Posts
    2

    Reading ints and doubles using scanf

    Hey,

    Doing a question where it requires me to read ints and doubles in the following format:

    4 5.0 6.0 7.0 3.0
    5 4.0 6.0 3.0 5.0 4.5

    and so on.
    So for 6 and 7 (max 100), they would have 6 and 7 doubles next to them respectively, all separated by whitespaces.

    My question is, how can i use scanf to read all the values and then assign the doubles into an array?

    EDIT: Accidentally double posted, sorry!
    Last edited by Gallivant; 04-23-2015 at 07:50 PM.

  2. #2
    Registered User
    Join Date
    Oct 2010
    Posts
    132
    Well, for example, to read two integers and one double value, you can use the string "%d%d%lf" with function scanf. Then you can manually assign the value of each variable to a position in an array using array[position] = "value of the current variable to be assigned".

  3. #3
    Registered User
    Join Date
    Apr 2015
    Posts
    2
    But if I did that and read one integer and two double values, it wouldn't be able to read one integer and three double values. I want to try and get a scanf that can handle any number of doubles that follow the first integer. Or is that not possible with just function scanf?

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    There are two basic approaches.

    First, you can do this by using fscanf() alone. You assume your input is an integer, followed by that many floating-point values, repeated until end of file. This is simple and straightforward to implement, and if this is an exercise, almost certainly the approach you want. It does also mean that inputs
    2 0.1 0.2
    1 0.3
    3 0.4 0.5 0.6
    and
    2 0.1 0.2 1 0.3 3 0.4 0.5 0.6
    are treated exactly the same; it does not matter whether the numbers are on the same line or not.

    Second, you can do this by using fgets(), or better yet, POSIX.1-2008 getline() (which unfortunately is not available in Microsoft Visual C). This way, you could keep track of the line number, print out erroneous lines, and enforce the format (that each line containing data must start with the integer, and follow with that many floating-point values). And, of course, easily allow comment lines (beginning with # or ; typically). To parse the line, you'd use len = -1; (void)sscanf(ptr, " %d%n", &values, &len); to parse the integer, and len = -1; (void)sscanf(ptr, " %lf%n", &value, &len); to parse each value. In both cases, len > 0 if the conversion was successful (numeric data as expected), and you advance to the next field by using ptr += len;.
    So, quite a bit more complicated, but much more user-friendly if used in a real application (because you can handle comments etc., and tell the user exactly where the problem is, if there is a problem with the input).




    Assuming you choose the fscanf() path, you need to remember the following things:
    1. fscanf() returns the number of successful conversions, or EOF at the end (or if a read error occurs). This allows you to check if the input was valid, and converted properly, and also to detect the end of the file; you can easily use a loop over "lines" (data sets that begin with the integer).
    2. %d converts an int, %lf converts a double.
      Any whitespace or newlines preceding the value is consumed (skipped, ignored).
    3. First convert the integer. Then, use that integer in a loop to convert each floating-point value, one by one.




    Quote Originally Posted by Gallivant View Post
    But if I did that and read one integer and two double values, it wouldn't be able to read one integer and three double values.
    You use one fscanf() call to read the integer. Then you use a loop (that loops that many times), to read each double using an fscanf() call, one by one.
    Last edited by Nominal Animal; 04-23-2015 at 08:22 PM.

  5. #5
    Registered User
    Join Date
    Apr 2015
    Posts
    2
    Ohh okay, I managed to get it!
    Thanks for the help!

  6. #6
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    I think more robust way to read such format would be using fgets+sscanf
    You read lines using fgets - then you extract numbers from the line using sscanf.

    This way - if one line is misformatted - it will not corrupt whole file reading after that point - you just discard the line and go to the next one.
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,667
    On a point of pedantry, scanf conversions can't detect numeric overflow.

    Consider strtol / strtod instead. Each has an 'endptr' feature which makes it quite easy to walk through a string performing your conversions.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    A very good point, Salem; one that I had not realized before.

    If using the "read a line, then parse the line as a string" approach, the (void)sscanf() stanzas I showed should be replaced with something like
    Code:
    #include <stdlib.h>
    #include <ctype.h>
    #include <errno.h>
    
    static int isendorspace(const char *const ptr)
    {
        return (ptr != NULL && (*ptr == '\0' || isspace(*ptr)));
    }
    
    ...
    
        char *ptr;  /* This points to the line to be parsed */
        long count;
        double value;
        char *endptr;
    
    ...
    
        endptr = ptr; errno = 0; count = strtol(ptr, &endptr, 10);
        if (endptr > ptr && isendorspace(endptr) && errno == 0) {
            /* successful conversion; have valid 'count' */
            ptr = endptr;
            /* Note: You should still check 'count' is valid, i.e. >= 1L. */
        }
    
    
            endptr = ptr; errno = 0; value = strtod(ptr, &endptr);
            if (endptr > ptr && isendorspace(endptr) &&
                ((errno == 0 && isfinite(value)) ||
                 (errno == ERANGE && value == 0.0))) {
                /* Successful conversion; have valid 'value'.
                 * It might have been so close to zero it rounded to zero, however.
                */
                ptr = endptr;
            }
    This looks a bit funky, but it should correctly convert even the exact values LONG_MIN, LONG_MAX, -HUGE_VAL, and HUGE_VAL, and stuff like Nan and Inf . The latter should accept values like 1e-500, which are too close to zero to be represented as nonzero doubles.

    The isendorspace() is a helper function that returns nonzero/true if and only if the pointer is valid and points to either a whitespace character (including newlines) or at the end of a string.

    Is Endor Space, indeed.

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Note: By "stuff like NaN and Inf", I meant that using isfinite() we can reject not-a-number and infinities. If you wish to accept NaNs and Infs in your data, just omit the && isfinite(value) part.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Only Accepting Ints and Doubles
    By Erik Ingvoldsen in forum C++ Programming
    Replies: 24
    Last Post: 02-20-2015, 04:52 PM
  2. scanf() and doubles?
    By Frankie15 in forum C Programming
    Replies: 4
    Last Post: 09-27-2011, 05:53 PM
  3. ints and doubles problem
    By iLLiCiT in forum C Programming
    Replies: 1
    Last Post: 12-04-2004, 03:42 PM
  4. Replies: 17
    Last Post: 08-03-2004, 02:16 PM