Thread: How to scan in multiple values from a text file

  1. #1
    Registered User
    Join Date
    Oct 2012
    Posts
    5

    How to scan in multiple values from a text file

    I am working on a project for my programming class, but I am having issues with getting values from a text file and assigning them to variables in my code. I have multiple lines of numbers in my text file, and each line tends to vary in the amount of floats and integers.

    Specifically:
    15 20 50 1.0 0.0
    5 4 5.0
    6 4 5.0
    7 4 5.0
    8 4 5.0
    -1

    There are several data sets as the one above.
    The first line consists of matrix and parameters for processing the values within the matrix, there are 5 values for the start of each data set. Afterwards there are several lines of 3 values. Those are values that will be put into the matrix (m,n,value). So the second line for example: array[5][4]=5.0.
    The last line of each data is always a -1. This flags the end of the data set.

    I have to create matrices multiple times, and I just need help scanning in the numbers.
    Here's what I have so far, but It's not working:

    Code:
    while(fscanf(fin,"%d %d %d %lf %lf",&m,&n,&itmax,&eps,&evalue) == 5)
    {
        /*some printing commands*/
    
    
        while(fscanf(fin,"%d",&i)==1)
        {
            if(i != -1)
            {
                fscanf(fin,"%d %lf",%j,%value);
            }
            else{break;}
        }
    }
    

  2. #2
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    Read from the file with fgets() ... to a string.
    Read from the string with sscanf()...

    Code:
    while (fgets(buffer, sizeof buffer, fin)) {
        chk = sscanf(buffer, "%d %d %d %lf %lf", &m,&n,&itmax,&eps,&evalue);
        if (chk == 1) {
            if (m == -1) {
                /* input terminated */
            } else {
                /* unexpected value */
            }
        } else {
            if (chk == 3) {
                /* rescan with a pointer do double in the 3rd position */
                chk2 = sscanf(buffer, "%d %d %lf", &i, &j, &value);
                if (chk2 == 3) {
                    /* use i, j, and value */
                } else {
                    /* this didn't happen */
                }
            } else {
                if (chk == 5) {
                    /* use m, n, itmax, eps, and evalue */
                } else {
                    /* invalid line */
                }
            }
        }
    }

  3. #3
    Registered User
    Join Date
    Oct 2012
    Posts
    5
    Question would I start off with:
    [code]
    while(fgets(buffer, sizeofbuffer, fin)!=NULL)
    [code/]

    if I wanted to keep this up till the end of the text file?

  4. #4
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    Why use fgets and then sscanf instead of fscanf?
    Fact - Beethoven wrote his first symphony in C

  5. #5
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    Quote Originally Posted by GenghisAhn View Post
    Question would I start off with:
    [code]
    while(fgets(buffer, sizeofbuffer, fin)!=NULL)
    [code/]

    if I wanted to keep this up till the end of the text file?
    Yes, that's the idea.

  6. #6
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    Quote Originally Posted by Click_here View Post
    Why use fgets and then sscanf instead of fscanf?
    fgets() retrieves and removes data from the stream (in groups of lines). That data is saved in a string and you can use it you like, for example, with sscanf()
    fscanf() retrieves and removes data from the stream (in heteregeneous groups depending on the format string). That data is put into variables and is not reusable.

    Recovering from errors in fgets() is easy.
    Recovering from errors in fscanf() in impossible.

  7. #7
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    I don't know if I agree with that... Especially because the OP is dealing with a file, not a stream.
    Fact - Beethoven wrote his first symphony in C

  8. #8
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    I agree with qny's approach, except that I'd use a nested loop like this:
    Code:
        char    linebuf[200]; /* Length of longest possible input line + 1 */
        char   *line;
    
        int     rows, cols, iterations;
        double  eps, err;
    
        int     row, col;
        double  value;
    
    
        /* Loop over matrices: */
        while (1) {
    
            /* Read new line from standard input. */
            line = fgets(linebuf, sizeof linebuf, stdin);
            if (!line) {
                /* No more input, no more matrices. */
                break;
            }
    
            /* Parse matrix declaration. */
            if (sscanf(line, "%d %d %d %lf %lf", &rows, &cols, &iterations, &eps, &err) < 5) {
    
                /* Bad line. Remove newline at end of line. */
                line[strcspn(line, "\r\n")] = '\0';
    
                fprintf(stderr, "%s: Invalid matrix declaration.\n", line);
    
                /* Break out of loop, just as if input had ended. */
                break;
            }
    
            /*
             * TODO: initialize a suitable matrix structure.
            */
    
            /* Sparse matrix element loop. */
            while (1) {
    
                /* Read new line from standard input. */
                line = fgets(linebuf, sizeof linebuf, stdin);
                if (!line) {
                    fprintf(stderr, "Warning: Premature end of input while reading matrix elements.\n");
                    break;
                }
    
                /* Matrix element? */
                if (sscanf(line, " %d %d %lf", &row, &col, &value) >= 3) {
    
                    /*
                     * TODO: Set matrix element at row 'row', column 'col' to 'value'
                    */
    
                } else
                if (sscanf(line, " %d", &row) >= 1 && row == -1) {
                    /* End of matrix elements */
                    break;
    
                } else {
                    /* Remove newline from the end of line. */
                    line[strcspn(line, "\r\n")] = '\0';
                    fprintf(stderr, "%s: Not a matrix element.\n", line);
    
                    /* Break out; treat the error as if it was a -1. */
                    break;
                 }
            }
        }
    You could also add a flag you set before each break in the inner loop, and check it at the end of the outer loop, to differentiate between input format errors and normal operation.

    If you wanted to add support for skipping empty lines (containing at most only white-space characters) and comment lines (lines beginning with a # or a ; ) all you need would be to add
    Code:
            /* Skip leading whitespace in the line. */
            line += strspn(line, "\t\n\v\f\r ");
    
            /* Skip comment lines and empty lines. */
            if (*line == '#' || *line == ';' || *line == '\0')
                continue;
    after each if (!line) statement.

  9. #9
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    948
    Quote Originally Posted by Click_here View Post
    I don't know if I agree with that... Especially because the OP is dealing with a file, not a stream.
    In C, a file is a stream of bytes.

  10. #10
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    In C, a file is a stream of bytes.
    Fair enough - I would think that there is a difference between, say, stdin/stdout/stderr (which I would call a stream), to a text/binary file (which I would call a file). However, there it is in section 7.19.3 File of the C99 Draft - You are definitely right in saying that a file is a stream.

    Recovering from errors in fgets() is easy.
    Recovering from errors in fscanf() in impossible.
    What I was trying to question was the above statement. How is using fscanf "impossible" to recover from when reading from a corrupt text file, but using fgets/sscanf is with regard to a text file? All fgets does is put the line in a buffer first - And then you are using sscanf which has the same problems as fscanf.

    The data won't be lost, it's a text file - Have you considered fseek?

    If there is a corrupt file, do you want to keep reading it? For the OP's code, I'd say no. If anything is unexpected, fputs a message on stderr and close the program.

    However, I am getting off track here disagreeing with the statement above saying "impossible" - For the record: I don't have a problem with the fgets solution, I have a problem with the word "impossible".
    Fact - Beethoven wrote his first symphony in C

  11. #11
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Click_here View Post
    All fgets does is put the line in a buffer first - And then you are using sscanf which has the same problems as fscanf.
    You can use multiple sscanf() calls on the same buffered line, to see which one works. If a pattern works partially, then fscanf() consumes the initial part; there is no way to get it back.

    Note that in POSIX systems, fseek() and ftell() do not work for pipes, sockets, or FIFOs. Some implementations might allow it in some cases, but the standard says it does not need to work for pipes, sockets, or FIFOs. There really is no way to get back the stuff fscanf() consumed. That's where the impossible comes from.

    For a practical example, assume you have an input line consisting of some number of records that start with a single-word name, followed by two numbers. A bug elsewhere causes a number to be misoutput -- this exact example is from Fortran code that specified a too-narrow output field. So, instead of having
    Code:
    automobiles 153398.23 142
    123foobar 0.0 0
    you have
    Code:
    automobiles *****.** 142
    123foobar 0.0 0
    If you use fscanf(..., " %63s %lf %d",...), it will fail at the asterisks, but will have consumed the "automobiles" part. How do you recover? How do you skip the asterisks? You don't, there is no simple, reliable way. The best you can do is fscanf(..., "%*[^\n]"); in an effort to discard the rest of the line.

    If you redo the fscanf(), you'll get the "*****.**" as the name string, 142 as the floating-point number, and 123 as the integer. The next fscanf() will read the name foobar, 0.0 as the floating-point number, and 123 as the integer.

    If you read the input line by line, you'll notice that the sscanf() could not convert all three values, so you output an error or a warning. Remove the trailing newline from the line, and tell the user that the line has bad format. If you saved the sscanf() return value (the number of conversions it did), you can even tell the user which field was in error. Then you discard the line, and go on to the next line.

    I also prefer to let my input lines contain comment lines starting with a # or a ; . With fscanf() that is very difficult, but reading line-by-line, it is trivial.
    Last edited by Nominal Animal; 10-29-2012 at 07:10 PM.

  12. #12
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    I appreciate that there are cases that fseek will not work. However, I was explicitly talking about text files.
    ... from a corrupt text file
    ... with regard to a text file
    ...
    it's a text file
    Changing the input to accept "**" does not fit the OP's specs. If the text file does not fit the specs, the program should terminate with an error - Just like your code did.

    And once again
    For the record: I don't have a problem with the fgets solution, I have a problem with the word "impossible".
    Fact - Beethoven wrote his first symphony in C

  13. #13
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    Quote Originally Posted by Nominal Animal View Post
    That's where the impossible comes from.
    Thanks Nominal Animal

    Quote Originally Posted by Click_here View Post
    I don't know if I agree with that... Especially because the OP is dealing with a file, not a stream.
    Ok ... I agree the "impossible" is a teeny tiny little too strong.
    I amend my statement to

    Recovering from data errors in fgets() is easy.
    Recovering from
    data errors in fscanf() is impossible in many situations.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. getting values from text file twice
    By Tigertan in forum C Programming
    Replies: 8
    Last Post: 09-04-2012, 01:02 AM
  2. Read text file, scan and store data into variables?
    By wisdom30 in forum C Programming
    Replies: 8
    Last Post: 04-18-2011, 11:23 PM
  3. C Function won't scan for user inputted values
    By kevin250 in forum C Programming
    Replies: 3
    Last Post: 10-20-2010, 08:47 PM
  4. get multiple values from INI file
    By hiya in forum C++ Programming
    Replies: 2
    Last Post: 06-12-2005, 03:00 PM
  5. scan multiple items in multidimensional array
    By requiem in forum C Programming
    Replies: 1
    Last Post: 04-17-2003, 03:02 PM