I have a text file containing two matrices in this form:
1 2 3 4 5 6 1 2 3 * 4 5 6 1 2 3]
I want to be able to read the dimension of the two matrices and the type of operation * + / -. I'd like to retrieve the dimension and read data at the same time.
In my code the get_dim() function goes through the data in the file to get the dimension of the two matrices. I don't know if there is a way to store the values of the matrices already at this point with dynamic memory allocation. With the function read_matrix() one that I know the dimension of the matrices I'm reading again the same data.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#define IN 1
#define OUT 0
struct matrix{
int rows;
int cols;
double *data;
};
void f(double x); /*truncate a double */
int get_dim(char *file, int *r, int *col);
void read_matrix(char *file, struct matrix *A, struct matrix *B);
void print_matrix(struct matrix *A);
void multiply(struct matrix *A, struct matrix *B, struct matrix *C);
int main (int argc, char *argv[])
{
int rows[2]= {0,0};
int cols[2]= {0,0};
int operation; /*type of operation 1 for * and 2 for + */
operation = get_dim(argv[1], rows, cols);
struct matrix A;
struct matrix B;
struct matrix C;
A.rows = rows[0];
A.cols = cols[0];
B.rows = rows[1];
B.cols = cols[1];
C.rows = rows[0];
C.cols = cols[1];
A.data = malloc(sizeof(double) * A.rows * A.cols);
B.data = malloc(sizeof(double) * B.rows * B.cols);
C.data = malloc(sizeof(double) * A.rows * B.cols);
read_matrix(argv[1],&A,&B);
print_matrix(&A);
printf("\n*\n");
print_matrix(&B);
printf("\n=\n");
multiply(&A,&B,&C);
print_matrix(&C);
free(A.data);
free(B.data);
free(C.data);
return 0;
}
void read_matrix(char *file, struct matrix *A, struct matrix *B){
int i,j;
FILE *fp;
int c=1;
if((fp = fopen(file, "r")) != NULL ){
for(i=0; i < A->rows; i++)
for(j=0; j < A->cols; j++)
fscanf(fp, "%lf", (A->data + (i * A->cols + j)));
/*skip the character operator line */
while(!isdigit(c))
c=fgetc(fp);
ungetc(c,fp);
for(i=0; i < B->rows; i++)
for(j=0; j < B->cols; j++)
fscanf(fp, "%lf", (B->data + (i * B->cols + j)));
}
fclose(fp);
}
int get_dim(char *file, int *rows, int *cols){
FILE *fp;
double a;
int c =1;
int n = OUT;
int op=0;
if((fp = fopen(file, "r")) == NULL ){
fprintf(stderr, "matrix: I cannot open %s\n",file);
exit(1);
}
while(fscanf(fp,"%lf",&a)){
if(n==OUT)
cols[0]++;
c=fgetc(fp);
if(isdigit(c))
ungetc(c,fp);
else if(c =='\n'){
rows[0]++;
n=IN;
}
else if(c=='*'){
op=1;
break;
}
}
n=OUT;
printf("\n");
while(!isdigit(c))
c=fgetc(fp);
ungetc(c,fp);
while(fscanf(fp,"%lf",&a)){
if(n==OUT)
cols[1]++;
c=fgetc(fp);
if(isdigit(c))
ungetc(c,fp);
else if(c =='\n'){
rows[1]++;
n=IN;
}
else if(c == ']'){
rows[1]++;
break;
}
}
fclose(fp);
return op;
}
void print_matrix(struct matrix *A){
int i,j;
/*printing the matrices*/
double *tmp = A->data;
for(i=0; i < A->rows; i++){
for(j=0; j < A->cols; j++){
f(*(tmp++));
}
printf("\n");
}
}
void multiply(struct matrix *A, struct matrix *B, struct matrix *C)
{
int i, j, k;
/*initialize C to 0*/
for (i=0; i< C->rows; i++){
for (j=0; j < C->cols; j++)
C->data[i * C->cols + j]=0;
}
// Multiplying matrix A and B and storing in C.
for(i = 0; i < A->rows; ++i)
for(j = 0; j < B->cols; ++j)
for(k=0; k < A->cols; ++k)
C->data[i * C->cols + j] += A->data[i * A->cols + k] * B->data[k * B->cols + j];
}
void f(double x)
{
double i,f= modf(x,&i);
if(f<.00001)
printf("%.f ",i);
else printf("%f ",x);
}
There is, but understand that even is you assume there are only two matricies separated by a single operator, to truly do a dynamic read of matricies of an unknown number of columns and rows takes being able to track the number of rows and columns for each matrix encountered in the file and careful attention to allocation and reallocation throughout the read process.
To simplify the approach you can first assume there will be less than or equal to 512 columns per-row (or some reasonable number that fits your data). This allows you to read a row of integer values into a temporary array before having to allocate storage for the row. (you can of course dynamically allocate and reallocate the temporary array to get to that point, but for purposes here, that just adds an additional set of conditional checks and reallocation -- where there are plenty already).
Now knowing the number of columns-per-row, (which you save in a variable to validate subsequent rows against), you can allocate storage for that row (and the remainder in that matrix until a row begins with a non-digit)
One way to simplify the storage of rows and columns of the matrix along with variables that store the number of rows and columns as a single using is to use a
struct
to hold the matrix and it's size. (this lends itself to 2 matricies, or any number you like) This allows you to allocate for an array of struct for any number of arrays to be read (or you can simply declare an array of 2 of them for your case and avoid the allocation/reallocation checks). For example:Where
a
is a pointer-to-pointer-toint
androw
andcol
hold the number of rows and columns in the allocated and filleda
. The choice of pointer-to-pointer allows native indexing asa[i][j]
in normal 2D fashion without having to map 2D indexing into a single offset. (you can do it either way, choice is yours)The base storage scheme is simple, you allocate (or statically declare) some number of
struct
, then allocate some initial number of pointers fora
, and as you read and convert each line into your temporary array, you allocate storage for each row, copy your temporary row to that block of memory, and assign the starting address of that block to the next available pointer. When the number of pointers used equals the number you initially allocated, yourealloc
more pointers and keep going. (when you are done with the arrays, make sure youfree()
everything you have allocated.That's basically it. The rest is just dotting your
i's
and crossing yourt's
on tracking what is allocated, and when a reallocation needs to occur. It's not complicated, it just takes careful attention to detail. The following implementation allocates (and will reallocate) for the structs (number of arrays) as well as the arrays themselves. It reads each line withfgets()
assuming each will fit into a1024
byte buffer (that too can be allocated and reallocated as required, but just as with the temporary array, that additional layer of allocation/reallocation has been omitted for example purposes).Each line is then converted into integers using
sscanf
into the temporary arraytmp
(you would ideally usestrtol
for the error detecting benefit, but that was omitted to simplify the example). A block of memory is then allocated and the integer fromtmp
are copied to the new block of memory and its address assigned as the next pointer in the current array.aidx
is used as your array of struct index (e.g. forarr[0], arr[1], ...
) When a non-digit is encountered as the first character in the line, it is taken as the operator between arrays and it is stored in an array ofchar
, the array indexaidx
is increment and filling of the next array proceeds.At the end, the arrays are printed out and all memory previous allocated is freed. Take the time to work through it and understand what is happening at each point and why. (use a piece of paper and pencil to track the iteration logic through -- often much better than staring at a computer screen)
Example Input File
Example Use/Output
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux
valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Work through the example. Understand that it doesn't matter whether you are allocating/reallocating for a 2x2 or 150x150 array, the validation and reallocation checks are the same which is what makes short input files like your seem deceptively over-complicated. They aren't, it just takes the same code to handle a 2x2 or 150x150. Let me know if you have further questions.
Some remarks on the first version of your question
Your loop
read up to the end of the file so both matrices, you need to detect the "*" so you do separate the first and the second matrices
you do not detect the number of columns, you need to read line per line, then to count the number of values per line (at least the first)
In
you can have an undefined behavior because you read up to 24 characters doing
fgets (str, 24, fp)
while you allocated only 6c != EOF
requires c is an int, not a charHere is a proposal, I do not know what are the kind of number you expect so I do not try to read number, I just look at elements separated by space, you can add a sscanf or equivalent to check each element is a number. I also suppose the operator is a character alone on its line (there is a newline just after)
Compilation and execution :
If you do not have getline replace
by for instance