how to count rows and columns of integers/doubles in a file?

Discussion in 'C Programming' started by Martin Joergensen, Mar 15, 2006.

  1. Hi,

    I have some files which has the following content:

    0 0 0 0 0 0
    0 1 1 1 1 0
    0 1 1 1 1 0
    0 1 1 1 1 0
    0 1 1 1 1 0
    0 0 0 0 0 0

    I'm making a function for a bigger program, that

    1) counts number of columns in each row
    2) verifies that there are the same number of columns in each row
    (if not: error + quit)
    3) increments row count after one row is finished
    4) stops execution if it encounters anything that is not an
    integer (later: should also work for doubles)

    My only problem is that I don't know how to switch line (carriage
    return) or how to detect it using scanf. Besides that, the
    program is almost finished. See below:
    - - - - - - - - - - - - - - - -
    #include <stdio.h>


    void int_getdata(char *filename, unsigned int *x, unsigned int
    *y);


    int main()
    {
    unsigned int n_x[3], n_y[3];

    int_getdata("unknowns.dat", &n_x[0], &n_y[0] );
    int_getdata("BC_types.dat", &n_x[1], &n_y[1] );
    // double_getdata("BC_values.dat", &n_x[2], &n_y[2] );

    return 0;
    }

    void int_getdata(char *filename, unsigned int *x, unsigned int
    *y)
    {
    FILE *fp;

    unsigned int old_nx;
    int int_readvalue, returnvalue;
    //double double_readvalue;

    old_nx = 0; /* reset */
    *x = 0;
    *y = 0;

    if ( (fp = fopen(filename, "r") ) == NULL)
    {
    printf("Cannot open file %s.\n", filename);
    system("PAUSE"); /* give the user at chance to see this
    error before the windows shuts down */
    exit(1);
    }

    /* get nx and ny */
    do{
    returnvalue = fscanf(fp, "%i", &int_readvalue); /* all
    input must be valid */

    if(returnvalue == 1) (*x)++; /* nx is getting larger */
    else{
    printf("Error: non-valid input found in %s.\n",
    filename);
    exit(1);
    }

    if(int_readvalue == '\n')
    {
    if(old_nx != 0 && old_nx != *x)
    {
    printf("ERROR: nx is not a constant in file
    %s!\n", filename);
    exit(1);
    }
    else /* we should now be sure that nx is constant and
    move on to the next input line */
    {
    old_nx = *x;
    y++; /* ny is getting larger */
    *x = 0;
    }
    }
    } while(returnvalue != EOF);

    printf("\nFinished reading from file %s: (x,y) = (%i,%i).\n",
    filename, *x, *y);
    fclose(fp); /* close input file, finished reading values in
    */
    }

    - - - - - - - - - - - - - - - -

    I hope somebody can help me solve this small problem, so I can
    finish the program....

    I also get these small warnings, but they are not critical:

    warning C4013: 'system' undefined; assuming extern returning int

    warning C4013: 'exit' undefined; assuming extern returning int


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    Martin Joergensen, Mar 15, 2006
    #1
    1. Advertisements

  2. Martin Joergensen

    Michael Mair Guest

    The end of a line is indicated by '\n' in C.
    If you are writing the file with a C programme, too, and
    the reading and the writing programme are in the same locale,
    then you do not have to take care of anything as long as you
    work with text streams. Only if you explicitly open a binary
    stream, then you have to look at the actual representation
    of '\n'.
    So, you can read in the number of lines and columns like this
    if you concentrate on 1) to 3). You need some sort of string
    buffer for 4) if you do not want to check yourself.

    int countRowsAndCols (FILE *pFile, unsigned *pRows, unsigned *pCols)
    {
    unsigned rows, cols, oldcols;
    int isConsistent = 1;

    /* define file here */

    rows = 0;
    if (countColsInNextLine(pFile, &oldcols) != EOF) {
    rows++;
    while (countColsInNextLine(pFile, &cols) != EOF) {
    rows++;
    if (cols != oldcols) {
    isConsistent = 0;
    /* Your error message here */
    }
    }
    }

    *pRows = rows;
    *pCols = oldcols;
    return !isConsistent;
    }

    In
    int countColsInNextLine (FILE *pFile, unsigned *pCols)
    {
    int c;
    unsigned cols = 0;

    do {
    c = getc(pFile);
    if (c != EOF && !isspace(c)) {
    cols++;
    do {
    c = getc(pFile);
    } while (c != EOF && !isspace(c));
    }
    while (c != EOF && c != '\n' && isspace(c)) {
    c = getc(pFile);
    }
    } while (c != EOF && c != '\n')

    *pCols = cols;
    return c;
    }
    you consume characters from the file until you find
    either a line break or the end of the file.
    You return the last result from getc(), i.e. EOF if the
    file ends. The functions are not tested.
    You could make life easier in the second function by
    introducing appropriate auxiliary functions.
    The above is not tested.
    This is not what you want. You compare the value of the read
    integer against the value of the int constant '\n'.
    Even though you can do certain things with the help of
    scan sets (%[] or %[^]), it is probably easier to read in
    a line and check the values with sscanf() or even strtoul()
    or strtod(). If you only need the originally indicated
    space -- non-space distinction, have a look at the suggested
    functions.
    We already lead the "how to read a line" discussion, so you
    can just look up the respective thread.

    They are. You forgot to #include <stddef.h>, so you did not get
    proper types for the functions.
    Especially, the arguments are not converted automatically and
    you don't get a warning if they cannot be converted. For these
    two functions, everything probably will work as intended but
    there is a serious error behind the warning.


    Cheers
    Michael
     
    Michael Mair, Mar 15, 2006
    #2
    1. Advertisements

  3. What do you mean by "columns"? Does "0 0 0 0 0 0" have 6 columns
    (numbers) or 11 (characters)?

    [snip]
    fscanf with a "%i" format reads an integer value after skipping any
    leading whitespace -- including new-lines. It gives no indication of
    what it skipped. If new-lines are significant, that's the wrong
    approach.

    It also accepts either decimal, octal, or hexadecimal input. Decide
    whether you want to allow numbers like "0xff", and whether you want
    "0123" to be interpreted in octal (yielding 83) or in decimal
    (yielding 123). Since you mentioned you'll eventually want the
    program to work with doubles, you probably don't want to accept octal
    or hexadecimal; you probably want "%d" rather than "%i". ("%d" and
    "%i" behave identically for the *printf functions, but differently for
    the *scanf functions).

    A much better approach is to use fgets() to read a line at a time,
    then use sscanf() to scan the resulting line. sscanf(), unlike
    fscanf(), doesn't consume its input; it's still there in the string,
    and if the sscanf() call fails (check its returned value), you can try
    again.

    fgets() has the disadvantage that it can only read a line up to a
    specified length. If the input line is longer than what you
    specified, fgets() will read only part of it; you can detect this by
    whether the line you just read ends in a '\n'. If you only want to
    allow input lines up to some maximum length, this isn't much of a
    problem -- but you should still decide what to do if an input line
    exceeds your maximum. If you want to handle arbitrarily long input
    lines, you can use CBFalconer's "ggets" (Google it).
     
    Keith Thompson, Mar 15, 2006
    #3
  4. fscanf will eat white space, including the \n that marks the end of
    line.

    You could use fgets to get a complete line then strtol to process each
    value for correctness.

    Remove del for email
     
    Barry Schwarz, Mar 16, 2006
    #4
  5. Thanks... Helped me a lot... Almost finished now... Se my other
    post.
    Oh, yeah... I hoped that it could read integers as well as
    '\n'... Seems like it can't. I just skips it..

    -snip-
    Ok... I added a little - I also want to skip commas.

    That was also so great about scanf, that it could check if all
    numbers were valid at the same time it counted...
    I found something on google:
    - - - - -- -
    #define LENGTH 20
    #define str(x) # x
    #define xstr(x) str(x)

    int rc;
    char array[LENGTH + 1];


    rc = scanf("%" xstr(LENGTH) "[^\n]%*[^\n]", array);
    if (!feof(stdin)) {
    getchar();
    }
    if (rc == 0) {
    *array = '\0';
    }


    /* rc will be either 1, 0, or EOF */

    - - - - -

    What the hell is happening here: "[^\n]%*[^\n]" ?
    No, I think it is stdlib.h, I needed... But thanks - you're
    always a big help to me...
    Ok, including stdlib.h seems to work...


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    Martin Joergensen, Mar 16, 2006
    #5
  6. -snip-

    What do you think about the following, based on Michael Mair's
    proposal?

    Just copy/paste - compiles well...
    - - - - - -
    #include <stdlib.h> /* system("PAUSE") */
    #include <stdio.h>
    #include <stddef.h>
    #include <ctype.h> /* for isspace */

    #define number_of_files 3

    /* prototypes */
    void getdata(char *filename, unsigned *pRows, unsigned *pCols);
    /* this is how the args should be */
    int countColsInNextLine (FILE *pFile, unsigned *pCols);


    int main()
    {
    int i;
    unsigned n_x[number_of_files], n_y[number_of_files]; /* one
    space for each data file */

    getdata("unknowns.dat", &n_x[0], &n_y[0] );
    getdata("BC_types.dat", &n_x[1], &n_y[1] );
    // getdata("BC_values.dat", &n_x[2], &n_y[2] );

    for(i=0; i<number_of_files; i++)
    printf("Data file number %i has properties: x = %u, y=
    %u\n", i, n_x, n_y);

    return 0;
    }

    //fgets( stringbuffer, 20, fp);
    //sscanf( stringbuffer, "%X", &var1);

    void getdata(char *filename, unsigned *pRows, unsigned *pCols) /*
    this is how the args should be */
    {
    FILE *pFile;

    unsigned rows, cols, oldcols;

    if ( (pFile = fopen(filename, "r") ) == NULL)
    {
    printf("Cannot open file %s.\n", filename);
    system("PAUSE"); /* give the user at chance to see this
    error before the windows shuts down */
    exit(1);
    }

    *pRows = 0;
    *pCols = 0;

    /* get nx and ny */
    cols = 0;
    rows = 0;
    if (countColsInNextLine(pFile, &oldcols) != EOF) { /* get
    oldcols for first row */
    rows++;
    while (countColsInNextLine(pFile, &cols) != EOF) {
    rows++;
    if (cols != oldcols) { /* verify that number of cols
    didn't change */
    printf("ERROR: Number of columns is not a
    constant in file: %s!\n", filename);
    exit(1);
    }
    }
    }

    *pRows = rows; /* update results */
    *pCols = oldcols;

    printf("\nFinished reading from file %s.\n", filename);
    fclose(pFile); /* close input file, finished reading values
    in */
    }

    int countColsInNextLine (FILE *pFile, unsigned *pCols)
    {
    int c;
    unsigned cols = 0;

    do {
    c = getc(pFile); /* get first character to start the loop
    */
    if (c != EOF && !isspace(c)) { /* first time non-space
    encountered, update cols */
    cols++;
    do { /* skip digits and commas */
    c = getc(pFile);
    } while (c != EOF && !isspace(c) && c != ',' && c !=
    '.'); /* comma should also be valid input in number */
    }
    while (c != EOF && c != '\n' && isspace(c)) { /* skip
    through spaces, but not '\n' ! */
    c = getc(pFile);
    }
    } while (c != EOF && c != '\n');

    *pCols = cols;
    return c;
    }
    - - - -
    It seems like there is small error in counting the rows..... I
    couldn't find the error before and I'm not sure I understood all
    the while loops completely - to help myself I added some
    comments.

    So there's just a little error left (that's the second time in
    this thread I write that - This time I hope it's true!) :)


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    Martin Joergensen, Mar 16, 2006
    #6
  7. -snip-

    I don't know why, but I can't debug that code very well... I put
    breakpoints in it but very often within the getdata-function, it just
    finishes the program ignoring my breakpoints....

    Why is that so?


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Mar 16, 2006
    #7
  8. Forget it... I forgot to add a compiler option and now it works.


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Mar 16, 2006
    #8
  9. Martin Joergensen

    Michael Mair Guest

    read up to LENGTH non-'\n' characters into array, consume
    arbitrarily many subsequent '\n's
    "Left, left -- oh, I meant the other left"
    Of course, you are right: I meant <stdlib.h>

    Cheers
    Michael
     
    Michael Mair, Mar 17, 2006
    #9
  10. Michael Mair wrote:
    -snip-
    Do you like my new program?

    I find it *REALLY* useful :)

    - - - - - - - - - - - -

    #include <stdlib.h> /* system("PAUSE") */
    #include <stdio.h>
    #include <stddef.h>
    #include <ctype.h> /* for isspace */

    #define max_number_of_files 50

    /* prototypes */
    void getdata(char *filename, unsigned *pCols, unsigned *pRows, int *count);
    int countColsInNextLine (FILE *pFile, unsigned *pCols, unsigned *pRows);
    void testwronginput(int c, unsigned cols, unsigned rows);

    int main()
    {
    int i, count;
    unsigned n_x[max_number_of_files], n_y[max_number_of_files]; /* one
    space for each data file */

    count = 0;
    getdata("unknowns.dat", &n_x[0], &n_y[0], &count );
    getdata("BC_types.dat", &n_x[1], &n_y[1], &count );
    getdata("BC_values.dat", &n_x[2], &n_y[2], &count );

    for(i=0; i<count; i++)
    printf("Data file number %i has properties: Columns (x) = %u,
    Rows y= %u\n", i+1, n_x, n_y);

    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    return 0;
    }


    void getdata(char *filename, unsigned *pCols, unsigned *pRows, int *count)
    {
    FILE *pFile;

    unsigned rows, cols, oldcols;

    printf("Opening file: %s.\n", filename);
    if ( (pFile = fopen(filename, "r") ) == NULL)
    {
    printf("Cannot open file %s.\n", filename);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }

    *pRows = 0;
    *pCols = 0;

    /* get nx and ny */
    cols = 0;
    rows = 0;
    if (countColsInNextLine(pFile, &oldcols, &rows) != EOF) { /* return
    oldcols for first row */
    rows++;
    while (countColsInNextLine(pFile, &cols, &rows) != EOF) { /*
    check row 2 -> EOF */
    rows++;
    if (cols != oldcols) { /* verify that number of cols didn't
    change */
    printf("ERROR: Number of columns is not a constant in
    file: %s\n\n", filename);
    printf("In line %u, the number of columns is %u
    cols.\n", rows, cols);
    printf("In the previous line it was counted to
    %u.\n\nPlease fix this problem now.\n", oldcols);
    exit(1);
    }
    }
    }

    if (cols != 0 && cols != oldcols) {
    printf("ERROR: Line %u (last line) consisted of %u
    columns.\nLine %u had %u columns.\n", rows, cols, rows-1, oldcols);
    exit(1);
    }

    *pRows = rows; /* update results */
    *pCols = oldcols;
    (*count)++;

    printf("Finished reading from file %s.\n\n", filename);
    fclose(pFile); /* close input file, finished reading values in */
    }

    int countColsInNextLine (FILE *pFile, unsigned *pCols, unsigned *pRows)
    {
    int c;
    unsigned cols = 0;

    c = getc(pFile); /* get first character to start the loop */
    do {
    testwronginput(c, cols, *pRows);
    if (c != EOF && !isspace(c)) { /* first time non-space
    encountered, update cols */
    cols++;
    do { /* skip digits and commas, after first digit */
    c = getc(pFile);
    testwronginput(c, cols, *pRows);
    } while (c != EOF && !isspace(c) || c == ',' || c == '.');
    /* comma should also be valid input in number */
    }
    while (c != EOF && c != '\n' && isspace(c)) { /* skip through
    blank spaces, but not '\n' ! */
    c = getc(pFile);
    testwronginput(c, cols, *pRows);
    }
    } while (c != EOF && c != '\n'); /* is the line finished or is EOF
    reached? */

    *pCols = cols;
    return c;
    }

    void testwronginput(int c, unsigned cols, unsigned rows) /* I tried
    isblank() but my system complained about it */
    {
    if (isalpha(c)) {
    printf("Error! Alphabetic character '%c' encountered before
    EOF\n", c);
    printf("Last succesful location read was: Line %u, Column
    %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }
    if (iscntrl(c) && c != 10) {
    printf("Error! Control character '%c' encountered before
    EOF\n", c);
    printf("Last succesful location read was: Line %u, Column
    %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }
    if (!isprint(c) && c != EOF && c != '\n') {
    printf("Error! Non-printing character '%c' encountered before
    EOF\n", c);
    printf("Last succesful location read was: Line %u, Column
    %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }
    }

    - - - - - - - - - - - -


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    =?ISO-8859-15?Q?Martin_J=F8rgensen?=, Mar 17, 2006
    #10
  11. -snip-

    I have a serious problem now!

    I thought everything was okay and was progressing well - So I now
    figured out how to do this program but now I want to include it in my
    bigger program and use number of cols + number of rows to malloc().

    So, I have a lot of arrays that depend on malloc() which again should
    depend on number of rows in x-direction = nx, and number of cols in
    y-direction = ny.

    So my big program starts with something like:
    - - - - - - - - - - - -

    int main(void)
    {

    /* prototypes */
    void int_fillinnumbers(int, int, int, int, int, int **);
    void double_fillinnumbers(double, int, int, int, int, double **);
    void int_printout(int, int, int, int, int **);
    void double_printout(int, int, int, int, double **);
    void conversion(int no, int *i, int *j); /* convert from number of
    interior cells to [j] format */
    void one_dim_double_printout(int startx, int stopx, double *array); /*
    for printing out temperatures from CG-solver */
    void integer_load_properties(char *filename, int **array);
    void double_load_properties(char *filename, double **array);

    pstSolveType pstSolve;


    // // // // // //

    /* STOP READING HERE! Now, I need the number of cols and rows from the
    data-files to dynamically initialize the correct 1D and 2D-array sizes.
    I need it here, because below malloc need number of cols */

    // // // // // //


    double **T = malloc((cols+1)*sizeof(double*)); /* alternative: int **T
    = malloc((cols + 1) * sizeof *T); */
    double **T_new = malloc((cols+1)*sizeof(double*));

    .. . . . . .
    .. . . . . . (later)
    .. . . . . .

    T[0] = malloc((cols+1)*(rows+1)*sizeof(double));
    T_new[0] = malloc((cols+1)*(rows+1)*sizeof(double));



    The problem is that I get a compiler error, something about a missing
    semi-colon. And I've got this error a couple of times before. I happens
    because I mix declarations and initialisation, somebody wrote...

    What the hell do I do now? Until now, rows and columns was just set to
    something using #define, but I don't like that solution with having to
    change input data several places, both in the #define line and also in
    each input file(s)...

    So, what I want is to include these lines:

    count = 0;
    getdata("unknowns.dat", &n_x[0], &n_y[0], &count );
    getdata("BC_types.dat", &n_x[1], &n_y[1], &count );
    getdata("BC_values.dat", &n_x[2], &n_y[2], &count );

    for(i=0; i<count; i++)
    printf("Data file number %i has properties: Columns (x) = %u,
    Rows y= %u\n", i+1, n_x, n_y);


    But then the compiler is complaining like hell, because I need to the
    return value of *n_x and *n_y for malloc(), *count is just a counter for
    number of read files...

    I really, *really* hope there is a good solution to this problem. I just
    have no idea what it is.

    Please let me know, if you have any ideas whatsoever. I almost can't
    believe that there's no way of dealing with this problem in a good way.


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    =?ISO-8859-15?Q?Martin_J=F8rgensen?=, Mar 17, 2006
    #11


  12. In most implementations, rows come before columns.
    Since you already have cols+1 pointers, each pointer only needs to
    point to rows+1 doubles, not (cols+1)*(rows+1).
    Missing semicolon errors are usually caused by an error preceding the
    actual line flagged. You need to show us all the code.


    Show the code. Your explanation is borderline meaningless.

    Remove del for email
     
    Barry Schwarz, Mar 18, 2006
    #12
  13. <snip>

    Here's some help. Compile this small program, and run it with the
    filename of your .dat file as the command line argument. Try all the
    cases you can think of, and see how the error handling works.


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

    enum Error {
    Success, /* All went well */
    TooManyCol, /* Too many columns */
    TooFewCol, /* Too few columns */
    ConvErr, /* Conversion failed */
    FileErr /* File read error */
    };

    /*
    * Parse a file to determine the number of rows and columns of integer
    * data. Lines containing only whitespace are ignored. If the return
    * value is not Success, the line number in the file where the problem
    * was encountered is written to *rows. If a fscanf conversion error
    * occurred, then the current position of 'fp' will be the position of
    * the problematic input.
    */
    enum Error parse(FILE *fp, int *rows, int *cols)
    {
    int line, count, ret, val;
    enum Error err = Success;

    *rows = *cols = 0;
    for(line = 1; ; line++) {
    for(count = 0; ; count++) {

    /* Discard spaces and tabs. */
    ret = fscanf(fp, "%*[ \t]");
    if(ret == EOF)
    break;

    /* Get the next character. */
    ret = getc(fp);
    if(ret == EOF)
    break;

    /* At the end of the line? */
    if(ret == '\n')
    break;

    /* No. Put back and fscanf. */
    ungetc(ret, fp);
    ret = fscanf(fp, "%d", &val);
    if(ret != 1) {
    if(ret == 0)
    err = ConvErr;
    break;
    }
    }

    /* Check for file error. */
    if(ret == EOF && ferror(fp))
    err = FileErr;

    /* If successful so far... */
    if(err == Success) {
    if(count != 0) {
    (*rows)++;
    if(*cols == 0)
    *cols = count;
    else {
    if(count < *cols)
    err = TooFewCol;
    else if(count > *cols)
    err = TooManyCol;
    }
    }
    }

    /* If an error or EOF, stop */
    if(err != Success || feof(fp))
    break;
    }

    /* On error, report line number. */
    if(err != Success)
    *rows = line;
    return err;
    }


    int main(int argc, char *argv[])
    {
    FILE *fp;
    int rows, cols;
    enum Error result;
    const char *file;
    int ret = EXIT_FAILURE;
    char buf[8];

    /* Check argument count. */
    if(argc != 2) {
    printf("usage: %s FILENAME\n", argv[0]);
    return ret;
    }

    /* Open the data file. */
    file = argv[1];
    fp = fopen(file, "r");
    if(fp == NULL) {
    printf("Error: cannot open %s.\n", file);
    return ret;
    }

    /* Try to parse the file, then interpret the result. */
    result = parse(fp, &rows, &cols);
    switch(result) {
    case Success:
    printf("%s: %d rows, %d columns\n", file,
    rows, cols);
    ret = EXIT_SUCCESS;
    break;

    case TooManyCol:
    printf("Error: %s line %d: too many columns.\n",
    file, rows);
    break;

    case TooFewCol:
    printf("Error: %s line %d: too few columns.\n",
    file, rows);
    break;

    case ConvErr:
    fscanf(fp, "%7s", buf);
    printf("Error: %s line %d: invalid input near \"%s\".\n",
    file, rows, buf);
    break;

    case FileErr:
    default:
    printf("Error: %s line %d: unknown file error.\n",
    file, rows);
    break;
    }

    /* Clean up and exit */
    fclose(fp);
    return ret;
    }


    Report back with any comments, questions, or bugs. I wrote it over
    lunch, so it may contain a bug or two.


    Mark F. Haigh
     
    Mark F. Haigh, Mar 18, 2006
    #13
  14. Damn... I think you're right....... I should change it - Only went good
    because number of rows = number of cols...
    But I *don't* have number of cols and number of rows from my program
    using the above implementation. In the above implementation, I just did
    a #define cols 8 (or whatever). As I wrote, the problem is that I don't
    want to change both #define rows and the input file. Only the input file..
    Well, I guess you saw my program but perhaps didn't notice it very much,
    so here I just took some parts to show you the structure of what I want
    (untested, but then you know what this is about):

    - - - - -

    /* prototypes */
    /* several ones bla.bla.bla. from the bigger program I have */

    /* more prototypes */
    void getdata(char *filename, unsigned *pCols, unsigned *pRows, int
    *count);
    int countColsInNextLine (FILE *pFile, unsigned *pCols, unsigned
    *pRows);
    void testwronginput(int c, unsigned cols, unsigned rows);


    int main(void)
    {
    /* read from file and get number of cols+rows */

    unsigned count; /* and a lot more var's, not necessary in this
    example */
    double **T, **T_new;

    count = 0; /* keep track of number of datafiles read */
    getdata("unknowns.dat", &n_x[0], &n_y[0], &count );
    getdata("BC_types.dat", &n_x[1], &n_y[1], &count );
    getdata("BC_values.dat", &n_x[2], &n_y[2], &count );

    /* error-testing if number of rows+cols is the same in all files,
    i.e: n_x[0] = n_x[1] = n_x[2] and the same for n_y */

    double **T = malloc((n_y[0]+1)*sizeof(double*)); /* n_y = rows */
    double **T_new = malloc((ny[0]+1)*sizeof(double*)); /* n_y = rows */

    /*. . . . . .
    .. . . . . . (later)
    .. . . . . . */

    T[0] = malloc((n_x[0]+1)*(n_y[0]+1)*sizeof(double)); /* nx= cols */
    T_new[0] = malloc((n_x[0]+1)*(n_y[0]+1)*sizeof(double)); /* nx= cols */
    }


    void getdata(char *filename, unsigned *pCols, unsigned *pRows, int *count)
    {
    FILE *pFile;

    unsigned rows, cols, oldcols;

    printf("Opening file: %s.\n", filename);
    if ( (pFile = fopen(filename, "r") ) == NULL)
    {
    printf("Cannot open file %s.\n", filename);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }

    *pRows = 0;
    *pCols = 0;

    /* get nx and ny */
    cols = 0;
    rows = 0;
    if (countColsInNextLine(pFile, &oldcols, &rows) != EOF) { /* return
    oldcols for first row */
    rows++;
    while (countColsInNextLine(pFile, &cols, &rows) != EOF) { /*
    check row 2 -> EOF */
    rows++;
    if (cols != oldcols) { /* verify that number of cols didn't
    change */
    printf("ERROR: Number of columns is not a constant in
    file: %s\n\n", filename);
    printf("In line %u, the number of columns is %u
    cols.\n", rows, cols);
    printf("In the previous line it was counted to
    %u.\n\nPlease fix this problem now.\n", oldcols);
    exit(1);
    }
    }
    }

    if (cols != 0 && cols != oldcols) {
    printf("ERROR: Line %u (last line) consisted of %u
    columns.\nLine %u had %u columns.\n", rows, cols, rows-1, oldcols);
    exit(1);
    }

    *pRows = rows; /* update results */
    *pCols = oldcols;
    (*count)++;

    printf("Finished reading from file %s.\n\n", filename);
    fclose(pFile); /* close input file, finished reading values in */
    }

    int countColsInNextLine (FILE *pFile, unsigned *pCols, unsigned *pRows)
    {
    int c;
    unsigned cols = 0;

    c = getc(pFile); /* get first character to start the loop */
    do {
    testwronginput(c, cols, *pRows);
    if (c != EOF && !isspace(c)) { /* first time non-space
    encountered, update cols */
    cols++;
    do { /* skip digits and commas, after first digit */
    c = getc(pFile);
    testwronginput(c, cols, *pRows);
    } while (c != EOF && !isspace(c) || c == ',' || c == '.');
    /* comma should also be valid input in number */
    }
    while (c != EOF && c != '\n' && isspace(c)) { /* skip through
    blank spaces, but not '\n' ! */
    c = getc(pFile);
    testwronginput(c, cols, *pRows);
    }
    } while (c != EOF && c != '\n'); /* is the line finished or is EOF
    reached? */

    *pCols = cols;
    return c;
    }

    void testwronginput(int c, unsigned cols, unsigned rows) /* I tried
    isblank() but my system complained about it */
    {
    if (isalpha(c)) {
    printf("Error! Alphabetic character '%c' encountered before
    EOF\n", c);
    printf("Last succesful location read was: Line %u, Column
    %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }
    if (iscntrl(c) && c != 10) {
    printf("Error! Control character '%c' encountered before
    EOF\n", c);
    printf("Last succesful location read was: Line %u, Column
    %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }
    if (!isprint(c) && c != EOF && c != '\n') {
    printf("Error! Non-printing character '%c' encountered before
    EOF\n", c);
    printf("Last succesful location read was: Line %u, Column
    %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see this error
    before the windows shuts down */
    exit(1);
    }
    }

    -snip-
    See above.



    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Mar 18, 2006
    #14
  15. "Barry Schwarz" <> skrev i en meddelelse
    -snip-
    Ok, forget everything I wrote earlier... I just need to know how
    to fix the following code (3 warnings, 2 errors).

    - - - -- - -- - - - -- - --
    #include <stdlib.h> /* system("PAUSE") */
    #include <stdio.h>
    #include <stddef.h>
    #include <ctype.h> /* for isspace */

    /* defintions */
    #define max_input_files 3

    /* more prototypes */
    void getdata(char *filename, unsigned *pCols, unsigned *pRows,
    int *count);
    int countColsInNextLine(FILE *pFile, unsigned *pCols, unsigned
    *pRows);
    void testwronginput(int c, unsigned cols, unsigned rows);


    int main(void)
    {
    /* read from file and get number of cols+rows */

    unsigned count, n_x[max_input_files], n_y[max_input_files];
    /* and a lot more var's, not necessary in this example */
    double **T, **T_new;

    count = 0; /* keep track of number of datafiles read */
    getdata("inputfile1.dat", &n_x[0], &n_y[0], &count );
    getdata("inputfile2.dat", &n_x[1], &n_y[1], &count );
    if(n_x[0] != n_x[1] && n_y[0] != n_y[1])
    exit(1);
    getdata("inputfile3.dat", &n_x[2], &n_y[2], &count );
    if(n_x[1] != n_x[2] && n_y[1] != n_y[2])
    exit(1);

    double **T = malloc((n_y[0]+1)*sizeof(double*)); /* n_y =
    rows */
    double **T_new = malloc((n_y[0]+1)*sizeof(double*)); /* n_y =
    rows */

    T[0] = malloc((n_x[0]+1)*(n_y[0]+1)*sizeof(double)); /* nx=
    cols */
    T_new[0] = malloc((n_x[0]+1)*(n_y[0]+1)*sizeof(double)); /*
    nx= cols */

    /* eventually test with T[4][6] = 959, and printf("%lf",
    T[4][6]); etc... */
    }


    void getdata(char *filename, unsigned *pCols, unsigned *pRows,
    int *count)
    {
    FILE *pFile;

    unsigned rows, cols, oldcols;

    printf("Opening file: %s.\n", filename);
    if ( (pFile = fopen(filename, "r") ) == NULL)
    {
    printf("Cannot open file %s.\n", filename);
    system("PAUSE"); /* give the user at chance to see this
    error
    before the windows shuts down */
    exit(1);
    }

    *pRows = 0;
    *pCols = 0;

    /* get nx and ny */
    cols = 0;
    rows = 0;
    if (countColsInNextLine(pFile, &oldcols, &rows) != EOF) { /*
    return
    oldcols
    for first row */
    rows++;
    while (countColsInNextLine(pFile, &cols, &rows) != EOF)
    { /*

    check row 2 -> EOF */
    rows++;
    if (cols != oldcols) { /* verify that number of cols
    didn't
    change */
    printf("ERROR: Number of columns is not a
    constant in file: %s\n\n", filename);
    printf("In line %u, the number of columns is %u cols.\n",
    rows, cols);
    printf("In the previous line it was counted to
    %u.\n\nPlease fix this problem now.\n", oldcols);
    exit(1);
    }
    }
    }

    if (cols != 0 && cols != oldcols) {
    printf("ERROR: Line %u (last line) consisted of %u
    columns.\nLine %u had %u columns.\n", rows, cols, rows-1,
    oldcols);
    exit(1);
    }

    *pRows = rows; /* update results */
    *pCols = oldcols;
    (*count)++;

    printf("Finished reading from file %s.\n\n", filename);
    fclose(pFile); /* close input file, finished reading values
    in */
    }

    int countColsInNextLine (FILE *pFile, unsigned *pCols, unsigned
    *pRows)
    {
    int c;
    unsigned cols = 0;

    c = getc(pFile); /* get first character to start the loop */
    do {
    testwronginput(c, cols, *pRows);
    if (c != EOF && !isspace(c)) { /* first time non-space
    encountered, update cols */
    cols++;
    do { /* skip digits and commas, after first digit
    */
    c = getc(pFile);
    testwronginput(c, cols, *pRows);
    } while (c != EOF && !isspace(c) || c == ',' || c ==
    '.');
    /* comma should also be valid input in number */
    }
    while (c != EOF && c != '\n' && isspace(c)) { /* skip
    through
    blank
    spaces, but not '\n' ! */
    c = getc(pFile);
    testwronginput(c, cols, *pRows);
    }
    } while (c != EOF && c != '\n'); /* is the line finished or
    is EOF
    reached? */

    *pCols = cols;
    return c;
    }

    void testwronginput(int c, unsigned cols, unsigned rows) /* I
    tried isblank() but my system complained about it */
    {
    if (isalpha(c)) {
    printf("Error! Alphabetic character '%c' encountered
    before EOF\n", c);
    printf("Last succesful location read was: Line %u,
    Column %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see
    this error
    before the windows shuts down */
    exit(1);
    }
    if (iscntrl(c) && c != 10) {
    printf("Error! Control character '%c' encountered before
    EOF\n", c);
    printf("Last succesful location read was: Line %u,
    Column %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see
    this error before the windows shuts down */
    exit(1);
    }
    if (!isprint(c) && c != EOF && c != '\n') {
    printf("Error! Non-printing character '%c' encountered
    before EOF\n", c);
    printf("Last succesful location read was: Line %u,
    Column %u\n", 1+rows, cols);
    system("PAUSE"); /* give the user at chance to see
    this error
    before the windows shuts down */
    exit(1);
    }
    }

    - - - -- - -- - - - -- - --

    Compiler complaints:

    int * differes in indirection.... Missing ';' before 'type....


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    Martin Joergensen, Mar 18, 2006
    #15
  16. Hi Mark,

    Thanks a lot for bringing another way of solving this problem... But I
    already solved this problem, so imagine changing your program such that
    i use malloc to initialize a new 2D array called T_new (untested):


    int main(int argc, char *argv[])
    {
    FILE *fp;
    int rows, cols;
    enum Error result;
    const char *file;
    int ret = EXIT_FAILURE;
    char buf[8];

    /* Open the data file (filename is fixed). */
    fp = fopen("testfile.dat", "r");
    if(fp == NULL) {
    printf("Error: cannot open %s.\n", file);
    return ret;
    }

    /* Try to parse the file, then interpret the result. */
    result = parse(fp, &rows, &cols);
    if(result != Success) {
    printf("Error in input file\n");
    exit(1)
    }

    /* this is the what I want: using number of cols+rows for malloc */
    double **T_new = malloc((rows+1)*sizeof(double*));
    T_new[0] = malloc((cols*rows)*sizeof(double));

    /* test: assuming input file has more than 4 rows and 5 columns */
    T_new[4][5] = 431.56;
    printf("test-value = %lf", T_new[4][5]");

    /* Clean up and exit */
    fclose(fp);
    return ret;
    }

    - - - - - - - - - - - -

    Since I already solved this problem (and my solution can also take files
    with double input elements such as 425,358 or 823.23 as individual
    elements, I didn't tested the above)... But you code is really nice to
    learn from, and very logically structured. I like that... It gives me
    som new ideas for future ways of solving problems.

    .... The above is just so you know what I'm asking for, in case you want
    to try and solve the problem... I just sended some "ready-to-go
    copy/paste code" to Barry Schwarz (18/3-06, time:16:04, but it didn't
    show up yet) that show I want to take the result from what corresponds
    to your variables called cols and rows, to malloc a 2D-array...


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Mar 18, 2006
    #16
  17. You show exactly two diagnostics at the very bottom of 200 lines of
    extremely poorly formatted code. You don't provide the complete
    error message and you don't identify to which lines the errors relate.
    (or if you did it was buried in your code to the point of
    invisibility). You need to give us a fighting chance to help you. Try
    all of the following:
    Limit your line length so your posting software does not break
    things up at an inconvenient place.
    Limit your use of vertical white space.
    Indent in a very consistent fashion, four characters usually
    works well.
    It is a common convention to code your #define in all caps. Just
    something to make it easier to read.
    Did you mean "and" or "or" here? If either mismatches do you quit or
    do you quit only if both mismatch?
    It will save you problems later on if you use sizeof *T.
    And sizeof *T[0] here.
    Use EXIT_FAILURE instead of 1 for portability.
    What did that look like on your system????
    Since you increment rows above, did you really intend for function
    count... to change its value also? But count... does not change rows.
    Why do you define the function to take a pointer when it really wants
    the value pointed to?
    At this point in time (first iteration of while loop), oldcols has not
    been initialized.
    Since && has higher precedence that ||, this is treated as
    (c != EOF && !isspace(c)) || (c == ',') || (c == '.')
    This is an A or B or C expression. If A is true, B and C will never
    be evaluated (called a short circuit evaluation). Consider either of
    the last two tests. If either is true, is it possible for A to be
    false? Obviously not. If A is false, c is either EOF (and B and C
    are both false also) or c is whitespace (and B and C are both false
    also). The bottom line is that B and C contribute nothing but a
    confusion factor. The truth of falseness of the expression is
    completely determined by A.
    Rather than trying to validate a numerical value yourself, you might
    consider using strtod to do the work for you. I think it will
    properly handle the decimal marker (, or .) based on locale.
    There is no isblank. If you want to test for blank, use ' '. If you
    want to test for whitespace, use isspace.
    What is 10? It may be \n on your system but it isn't on mine. Don't
    use magic ascii codes. Use the escape sequence if you can.
    By definition, control character will not print with %c. Use %d so
    the user can determine which control character was found.
    A non-printing won't display properly with %c. Use %d here also.
    Sorry, your formatting makes it impossible to compile your code and I
    didn't spot these in my review.

    Remove del for email
     
    Barry Schwarz, Mar 18, 2006
    #17
  18. Martin Joergensen

    Pedro Graca Guest

    I copied lines with problems and inserted a `#ifdef pmg` to the
    changed lines. After a few compilation runs gcc output is

    $ c90 -Dpmg foo.c
    foo.c: In function `countColsInNextLine':
    foo.c:135: warning: suggest parentheses around && within ||


    if I don't define pmg (so it's the code you posted), gcc output is

    $ c90 foo.c
    foo.c: In function `main':
    foo.c:42: warning: pointer targets in passing arg 4 of `getdata' differ in signedness
    foo.c:43: warning: pointer targets in passing arg 4 of `getdata' differ in signedness
    foo.c:47: warning: pointer targets in passing arg 4 of `getdata' differ in signedness
    foo.c:56: error: redeclaration of `T'
    foo.c:39: error: `T' previously declared here
    foo.c:56: warning: ISO C89 forbids mixed declarations and code
    foo.c:57: error: redeclaration of `T_new'
    foo.c:39: error: `T_new' previously declared here
    foo.c:39: warning: unused variable `T'
    foo.c:39: warning: unused variable `T_new'
    foo.c: In function `countColsInNextLine':
    foo.c:135: warning: suggest parentheses around && within ||

    Will you provide a working email address so that I can
    mail you my changes?
     
    Pedro Graca, Mar 19, 2006
    #18
  19. Ok, but what is pmg?
    Sure, or else you could just post it here... But just remove ".spam"
    before the @ and remove "spam." after the "@" - that's my e-mail address
    (I just did it that way to avoid the spam robots, so please don't post
    the full address without "concealment"). At this moment, I don't get
    spam to that address and I would like it to be that way, if possible.


    Best regards / Med venlig hilsen
    Martin Jørgensen
     
    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Mar 19, 2006
    #19
  20. I certainly don't think you've solved the problem. In fact, I'm not
    even convinced you've managed to state the problem coherently. The
    code you've posted thus far is confused, not robust, and
    unmaintainable.

    Don't be offended. Nobody is born knowing C. You're making a
    sustained effort, and that is why we're willing to spend time to help.

    There's a lot of things we don't know about what you're trying to do.
    But we do know that:

    1. You are a student, and this may be a homework assignment. If this
    is the case, you're trying to learn how to use C to solve real-world
    problems. So we can conclude that you should be working hard to make
    your program more clear, robust, and maintainable so that you learn a
    lot about C, and as a result, get a good grade.

    Or...

    2. You are writing a program that you hope others will find useful.
    If this is the case, you need to ensure that your program is robust (a
    non-robust program is not very useful) and clearly-written and
    maintainable (so that other people can understand how the program
    works, and can extend it or fix bugs in it).

    Either way, you need to work toward the same goals of clarity,
    robustness, and maintainability. A solution that does not address all
    of these is not a solution at all.
    Because you have not assured us that your problem is not homework, I'm
    not going to post code that does exactly what you need. It's just a
    push in the right direction, and is easily extendable to do exactly
    what you need.

    Notice that nowhere have you:

    1. Described concisely the format of the file you wish to be able to
    parse. Do you control the file format? Or is it generated by another
    program not under your control?

    2. Described what you wish to do with the file. What's the point of
    this exercise? What's the big picture? I take it you want to read it
    into a multidimensional array so you can manipulate it somehow. What
    for?

    You have to help us so we can help you. We're not mind readers!

    <snip>

    Mark F. Haigh
     
    Mark F. Haigh, Mar 19, 2006
    #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.