0

I'm trying to make a program that reads a text file and fills a matrix inside a struct, but i'm having some problems.

#include <stdio.h>

#define MAX 100

typedef struct {
    int matrixgrafh[MAX][MAX];
} matrix;

int LoadMatrix(matrix* m);

main()
{
    int j,k;
    matrix m;

    LoadMatrix(&m);
    for(j = 0;j < 20;j++)
    {
        for(k = 0;k < 20;k++)
        {
            printf("%d",m.matrixgrafh[j][k]);
        }
    }
}

int LoadMatrix(matrix* m)
{
    FILE *stuff;
    int j,k;
    char c;

    if((stuff=fopen("matrix.txt","r"))==NULL)
    {
        printf("File missing...");
        exit(1);
    }
    while (!feof(stuff))
    {
         do
         {
            c = fgetc(stuff);
            if (c == '1' || c == '0' && c != '\n' && !feof(stuff))
            //corrected c == '1' && c == '0' to c == '1' || c == '0'
            {
                for(j = 0;j < 20;j++)
                {
                    for(k = 0;k < 20;k++)
                    {
                        m->matrixgrafh[j][k] = c;
                    }
                }
            }
         } while (!feof(stuff));
    }
    return m->matrixgrafh;
}

The idea is to read a matrix from the text file, like this:

1 1 1

1 0 1

0 0 1

and fill the matrix the same way.

The problem is that the matrix is not filling right, it shows 494949494949... I was expecting to loop through the matrix and it contains the same value as the text file.

  • What is your actual question/problem? "Having problems" is not really an adequate problem description. And "any ideas?" is not really a specific question. Please state the input (which you have done), the expected behaviour, the actual behaviour and what you have already done to debug it. – kaylum Oct 29 '15 at 19:40
  • There is a problem with this if: `if (c == '1' && c == '0' && c != '\n' && !feof(stuff))`, c cannot be 1 and 0 at the same time, so it will never be true. – mch Oct 29 '15 at 19:41
  • my problem is that the matrix is not filling right.I corrected the && and put a || but its not filling it right. – Drakoris Urileth Oct 29 '15 at 20:07

2 Answers2

1

You should first look at the question Why is “while ( !feof (file) )” always wrong? While not directly causing your problem, you will want to avoid this approach to reading text from files due to the pitfalls it creates.

Next, when you are reading lines of data from a file, it is generally best to read a line at a time. While there is nothing wrong with reading a character at a time, with character-oriented input, your code when reading numbers will be significantly longer due to the need to check each character, find the beginning of each number, temporarily buffer the digits in a null-terminated string, and then convert the buffered digits to a number (even with '0' and '1', they still have to be converted from characters to numbers)

That said, your primary tools for line-oriented input are fgets and getline (each have strengths and weaknesses). For a simple task like this, fgets is more than sufficient. So you read a line, then what? You have to separate the line into strings containing the digits for each number and then convert the strings to numbers. The C library has tools specially created for this task. Look at the strtol (string to long), strtoul (unsigned long), strtof (float), etc..

Each of the strtoX functions takes a pointer to the string to convert, and a second pointer (an endptr) which it fills with the address for the very next character in the string following the number it just converted. (the integer functions also take the base as an argument allowing conversion to base 10, 16, etc..)

Why is the endptr so important? It allows you to work down your line, containing an unknown number of numbers, converting each one in sequence until the end of line is reached. The strtoX functions offer good error detection and reporting allowing you to work down the line read from the file, and stop when you reach the end (as well as reporting on failed conversions)

Putting the line-oriented input with fgets together with strtol (in your case) will allow you to read each line in your file and convert each number in the file into a number in your array within the struct.

I don't know if you can add to your struct, but it you are going to the trouble of using a struct to hold your array, it sure makes sense to also store the number of rows and cols read from the file as well. That makes life easier in a number of ways when passing the struct to functions, printing, etc. Something like:

typedef struct {
    int matrixgrafh[MAX][MAX];
    size_t rows;
    size_t cols;
} matrix;

is all you need.

Now lets put it all together in a working example. I have put the strtol call in a function just to keep from cluttering the logic in LoadMatrix up with error checks, etc.. I have also left your existing code in the function, but commented so you can see the differences:

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

#define MAX 100
#define MAXLN 256

typedef struct {
    int matrixgrafh[MAX][MAX];
    size_t rows;
    size_t cols;
} matrix;

int LoadMatrix (matrix * m);
long xstrtol (char *p, char **ep, int base);

int main (void)
{
    size_t j, k;
    matrix m;

    if (LoadMatrix (&m) == -1) {
        fprintf (stderr, "error: LoadMatrix failed.\n");
        return 1;
    }

    printf ("\nThe %zux%zu matrix read from file is:\n\n", m.rows, m.cols);    
    for (j = 0; j < m.rows; j++) {
        char *pad = " [ "; \
        for (k = 0; k < m.cols; k++) {
            printf ("%s%2d", pad, m.matrixgrafh[j][k]);
            pad = ", ";
        }
        printf (" ]\n");
    }
    putchar ('\n');

    return 0;
}

int LoadMatrix (matrix *m)
{
    FILE *stuff;
    char line[MAXLN] = {0};     /* line buffer */
    char *p, *ep;     /* pointers for strtol   */
    size_t row = 0;   /* simple row counter    */
    // char c;

    if ((stuff = fopen ("matrix.txt", "r")) == NULL) {
        printf ("File missing..."); /* should be fprintf */
        exit (1);
    }

    while (fgets (line, MAXLN, stuff)) /* read each line in file */
    {
        size_t col = 0;    /* initialize variables for each row */
        p = ep = line;
        errno = 0;

        /* skip to first digit in line */
        while (*p && *p != '-' && (*p < '0' || *p > '9')) p++;

        while (errno == 0)
        {   /* read each number in row */
            m->matrixgrafh[row][col++] = (int)xstrtol (p, &ep, 10);

            if (col == MAX) { /* check col against MAX */
                fprintf (stderr, "LoadMatrix() error: MAX columns reached.\n");
                break;
            }

            /* skip to next number */
            while (*ep && *ep != '-' && (*ep < '0' || *ep > '9')) ep++;
            if (*ep) p = ep;
            else break;
        }

        if (row == 0) m->cols = col;    /* set the number of colums in each row */
        if (col != m->cols) {           /* validate each row against first      */
            fprintf (stderr, "LoadMatrix() error: invalid number of columns, row '%zu'.\n",
                    row);
            fclose (stuff);
            return -1;
        }
        row++;

        if (row == MAX) { /* check col against MAX */
            fprintf (stderr, "LoadMatrix() error: MAX rows reached.\n");
            break;
        }
    }

    fclose (stuff);

    m->rows = row;

    return 0;

    /* see why 'while (!feof (file))' is always wrong */
    // while (!feof (stuff)) {
    //     do {
    //         c = fgetc (stuff);
    //         if (c == '1' || c == '0' && c != '\n' && !feof (stuff))
    //             //corrected c == '1' && c == '0' to c == '1' || c == '0'
    //         {
    //             for (j = 0; j < 20; j++) {
    //                 for (k = 0; k < 20; k++) {
    //                     m->matrixgrafh[j][k] = c;
    //                 }
    //             }
    //         }
    //     } while (!feof (stuff));
    // }
    // return m->matrixgrafh;
}

/** a simple strtol implementation with error checking.
 *  any failed conversion will cause program exit. Adjust
 *  response to failed conversion as required.
 */
long xstrtol (char *p, char **ep, int base)
{
    errno = 0;

    long tmp = strtol (p, ep, base);

    /* Check for various possible errors */
    if ((errno == ERANGE && (tmp == LONG_MIN || tmp == LONG_MAX)) ||
        (errno != 0 && tmp == 0)) {
        perror ("strtol");
        exit (EXIT_FAILURE);
    }

    if (*ep == p) {
        fprintf (stderr, "No digits were found\n");
        exit (EXIT_FAILURE);
    }

    return tmp;
}

Take a look and let me know if you have questions. The input used, the compile string and the output you should expect are as follows:

Input

$ cat matrix.txt
1 1 1
1 0 1
0 0 1

Compile

gcc -Wall -Wextra -o bin/matrix_struct matrix_struct.c

Output

$ ./bin/matrix_struct

The 3x3 matrix read from file is:

 [  1,  1,  1 ]
 [  1,  0,  1 ]
 [  0,  0,  1 ]
Community
  • 1
  • 1
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Upvoted for teaching a lot of good stuff, but a couple critiques: (1) `feof()` is misused here but it's actually not what's preventing the OP from getting their intended answer (and it's not always wrong, strictly speaking, so much as it's usually unnecessary when the I/O function return values are properly checked), and (2) for something that smells a lot like homework, writing a lot of code in an answer is perhaps excessively generous. :) – David Duncan Oct 29 '15 at 22:34
0

The key issues are these:

  • Each entry is being displayed as 49 instead of 1 because 49 is the ASCII value of the character '1' and the program isn't converting from the ASCII value to an integer. Search the web for an ASCII table for a bit more context.
  • For each character being read from matrix.txt, you're filling the 20x20 matrix with that character's value. So the only value that really matters is the final 0 or 1 read from matrix.txt. (In this case, it's 1, or 49 per the first point.)
David Duncan
  • 1,225
  • 8
  • 14