1

I have an issue with input in my homework. On stdin, I will get a specifically formatted input.

In first line, there will be 2 integers, that determine the size of a matrix (rows and cols). All the lines after represent rows of the matrix.

I essentially want to do something like getline(), but I don't want to use getline(). In fact I can't, its forbidden in the homework. Therefore I have to scan int by int (or char by char I guess). The issue here is I need it to be bulletproof (almost). Dummy-proof at least.

I'm imagining a big while loop that keeps going until EOF and inside that another loop (perhaps?) which always reads a line, saves it to my allocated matrix and carries on to the next. I'm aware that I'm supposed to be checking for '\n', but I kind of lack the ability to think of a solution today.

Here's what I'm working with: My matrices are a structure.

struct Matrix{
    int nrows;
    int ncols;
    int** matrix;
};

I then have multiple functions.

A function to dynamically allocate space for the matrix of specific size:

struct Matrix init_matrix(int r, int c)
{
    struct Matrix mat;
    mat.nrows = r;
    mat.ncols = c;
    mat.matrix = calloc(r, sizeof(int *));
    for(int i = 0; i < r; ++i)
    {
        *(mat.matrix+i) = calloc(c, sizeof(int));
    }

    return mat;
}

A function to free the previously allocated space:

void free_matrix(struct Matrix mat)
{   
    int top = mat.nrows;
    for(int i = 0; i < top; ++i)
    {
        free(mat.matrix[i]);
    }
    free(mat.matrix);
}

Those 2 functions work perfectly fine.

Now I'm trying to make a function create_matrix(void) (at least I think it shouldn't take any args), that will read the input I'm supposed to receive, for example:

3 3
1 2 3
4 5 6
7 8 9

when the function reads the input, it could tell if the input is incorrect or is in incorrect format and exit the program with corresponding exit value (like 100 f.e.) If the input is correct and in correct format, it calls init_matrix() and then saves input to the matrix.

For your deeper understanding: the whole input I'm supposed to receive is: matrix A (like above, size in first line, values in lines after) an operation (+,-,*) matrix B

Then execute the operation (A*B, A+B etc.). I'm trying to make most things into functions, so the main would be very simple, f.e.

int main(int argc, char *argv[])
{
    struct Matrix mat1 = create_matrix();
    char operation = get_operation();
    struct Matrix mat2 = create_matrix();
    struct Matrix result = compute(mat1,mat2, operation);
    return 0;
}

Something in those lines, if you get me. The thing is I want to make the program complex enough so that I could later edit it to handle a bigger sequence (up to 100) of matrices than just two. Right now I could do it the dirty way, make it work for two matrices with one operation, but that's not what I really want.

halfer
  • 19,824
  • 17
  • 99
  • 186
Welsy
  • 77
  • 11
  • 1
    So what is the question? Your plan looks fine so far. – Ian Abbott Nov 23 '17 at 15:11
  • The create_matrix function structure. Can’t really imagine how to first scan the size values and check that they were in the firstline and after do a bigger scan that would do the rest. I kind of suspect that there will be 2 scans, but thats about all I can imagine. – Welsy Nov 23 '17 at 15:13
  • Use `fgets()` to read lines if you can’t use `getline()`. If you can’t use that either, then write your own surrogate for `getline()`; it isn’t hard. The `scanf()` functions skip white space with abandon, including new lines, and cannot simply be used to ensure that data is on the correct lines. Use `sscanf()` in a loop once you’ve got a line to process. – Jonathan Leffler Nov 23 '17 at 15:22
  • It sounds like you want to make `compute` handle an arbitrary number of matrices. You could use a variadic function using [`stdarg.h`](http://port70.net/~nsz/c/c11/n1570.html#7.16): `struct Matrix compute(char, struct Matrix, ...);`. – ad absurdum Nov 23 '17 at 15:23
  • Your design for `create_matrix()` is probably sub-optimal. You either need to pass in a file stream or a file name so that it knows where to get the data from. Relying on global variables to convey that information is icky. If it will read from standard input, then maybe it is OK, but it is not very flexible; it would be better to supply the name or the stream to be read as an argument to `create_matrix()`. For the rest, it is basic file reading operations — more fiddly than difficult. – Jonathan Leffler Nov 23 '17 at 17:11
  • @JonathanLeffler i can only use stdin, files arent an option sadly – Welsy Nov 23 '17 at 17:19
  • You can always write a general purpose function that takes a file stream argument and pass `stdin` as the argument. The general purpose code is probably a better design. The only restriction is that the general purpose code shouldn't make unwarranted assumptions about the file stream, such as that `fseek()` is an option. If standard input comes from a pipe or a terminal, you can't seek on the stream, for example. So, you should be able to write the code. You seem to know the specification. If there's a problem with the code, show it and we can help you fix it. – Jonathan Leffler Nov 23 '17 at 17:28
  • @JonathanLeffler I'm very new to C, only about 2 months since I've started learning, so I don't really know how would I be using the fact I'm passing a stream. I guess I could be using fscanf(stdin,...) and getc instead of getchar? – Welsy Nov 24 '17 at 14:50
  • You would have a function `read_matrix(FILE *fp){ … }` and inside that you’d use `getc(fp)` and `fscanf(fp, …)` etc to do the reading operations. You’d call `read_matrix(stdin)` to make it read from standard input, or you’d use `FILE *fp = fopen(filename, “r”); Matrix m = read_matrix(fp);` (with error checking) to read from a file, etc. – Jonathan Leffler Nov 24 '17 at 15:44
  • @JonathanLeffler I managed to sucessfully break down the input of matrix in lines (strings). I get the first line, initiliaze the matrix to correct size from values in first line, then I do a while loop that breaks down the other lines into strings. Where I seem to struggle is breaking the string down to integers. I can do it for only numbers > 0 (though its a dirty solution), the moment negative numbers come in, Im screwed. I simply need to get a known number (can be high) of integers from a string. – Welsy Nov 24 '17 at 21:40
  • See [Using `sscanf()` in a loop](https://stackoverflow.com/questions/3975236/how-to-use-sscanf-in-loops) for information on how to handle the data in a string. There are other ways too — using `strtol()` or similar also works well. – Jonathan Leffler Nov 24 '17 at 22:17

1 Answers1

1

Well, here's how I solved it. It works. It's not anywhere close to perfect, but it works, upload system took it and gave it full amount of points, so I'm satisfied.

struct Matrix read_matrix(FILE *fp)
{
    struct Matrix mat;
    //FIRST LINE
    int ch;
    int i = 0;
    int n = 20;
    char* line = calloc(n,sizeof(char));
    while((ch = fgetc(fp)) != EOF && ch != '\n')
    {
        *(line + i++) = ch;
    }
    *(line + n-1) = '\0';
    int r,c;
    int k = sscanf(line,"%d %d", &r, &c);
    if(k != 2)
    {
        fprintf(stderr, "Error: Chybny vstup!\n");
        exit(100);
    } 
    free(line);
    //MATRIX
    line = calloc(c, sizeof(int));
    mat = init_matrix(r, c);
    i = 0;
    r = 0;
    while(r < mat.nrows && (ch = fgetc(fp)))
    {
        if(ch == '\n' || ch == EOF)
        {
            *(line + i) = '\0';
            int offset;
            char *data = line;
            for(int j = 0; j < mat.ncols; ++j)
            {
                int d = sscanf(data, " %d%n", &mat.matrix[r][j], &offset);
                if(d != 1){
                    fprintf(stderr, "Error: Chybny vstup!\n");
                    exit(100);
                }
                data += offset;
            }
            i = 0;
            ++r;
            if(ch == EOF){ 
                break;
            }
        } else
        {
            *(line + i++) = ch;
        }
    }
    free(line);
    return mat;
}
Welsy
  • 77
  • 11