从文件中读取矩阵并获取其维度

时间:2019-05-26 09:49:29

标签: c file matrix

我有一个文本文件,其中包含以下两种形式的矩阵:

1 2 3
4 5 6
1 2 3
*
4 5 6
1 2 3]

我希望能够读取两个矩阵的维和运算类型* + /-。我想同时检索维度并读取数据。

在我的代码中,get_dim()函数遍历文件中的数据以获取两个矩阵的维数。我不知道此时是否有一种方法可以通过动态内存分配来存储矩阵的值。使用read_matrix()函数,我知道矩阵的维数,因此我再次读取了相同的数据。

#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);    
}

2 个答案:

答案 0 :(得分:1)

有关您的问题的第一个版本的一些评论

您的循环

 while (chr != EOF)
 {
   //Count whenever new line is encountered
   if (chr == '\n')
       rows++;
   //take next character from file.
   chr = getc(fp);
 }

读取文件的末尾,因此两个矩阵都需要检测到“ *”,因此您要分开第一和第二个矩阵

您不检测列数,需要读取每行的行数,然后计算每行的值数(至少是第一行)

str = (char *) malloc(6 * sizeof(char));

if( fgets (str, 24, fp)!=NULL ) {

您可能会遇到未定义的行为,因为您在分配6个字符的同时最多读了24个字符,fgets (str, 24, fp)

c != EOF要求 c int ,而不是 char


这是一个建议,我不知道您希望使用哪种数字,所以我不尝试读取数字,我只是查看用空格分隔的元素,可以添加 sscanf 或等同于检查每个元素是否为数字。我还假设运算符在其行上是一个单独的字符(紧随其后的是换行符)

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

int readMat(FILE * fp, int * cols, int * rows, char * oper)
{
  *cols = *rows = 0;
  *oper = 0;

  char * lineptr = NULL;
  size_t n = 0;

  while (getline(&lineptr, &n, fp) > 0) {
    if (((*lineptr == '*') || (*lineptr == '/') || (*lineptr == '-') || (*lineptr == '+'))
        && (lineptr[1] == '\n')) {
      *oper = *lineptr;
      break;
    }

    if (strtok(lineptr, " ") == NULL) {
      /* invalid input */
      *cols = 0;
      break;
    }

    *rows += 1;

    int c = 1;

    while (strtok(NULL, " ") != NULL)
      c += 1;

    if (*cols == 0)
      *cols = c;
    else if (*cols != c) {
      /* invalid input */
      *cols = 0;
      break;
    }
  }

  free(lineptr);

  return *cols != 0;
}

int main(int argc, char ** argv)
{
  if (argc != 2)
    printf("Usage: %s <file>\n", *argv);
  else {
    FILE * fp = fopen(argv[1], "r");

    if (fp == NULL)
      fprintf(stderr, "cannot open '%s'\n", argv[1]);
    else {
      int cols, rows;
      char oper;

      if (!readMat(fp, &cols, &rows, &oper))
        fputs("invalid first matrice", stderr);
      else if (oper == 0)
        fputs("operator is missing", stderr);
      else {
        printf("first matrice has %d columns and %d rows\noper is %c\n", cols, rows, oper);

        if (!readMat(fp, &cols, &rows, &oper))
          fputs("invalid second matrice", stderr);
        else if (oper != 0)
          fputs("unexpected operator", stderr);
        else
          printf("second matrice has %d columns and %d rows\n", cols, rows);
      }
      fclose(fp);
    }
  }

  return 0;
}

编译和执行:

pi@raspberrypi:/tmp $ gcc -g -pedantic -Wall -Wextra m.c
pi@raspberrypi:/tmp $ cat m
1 2 3
4 5 6
1 2 3
*
  44  5.2   6e12
-1     2   333
pi@raspberrypi:/tmp $ ./a.out m
first matrice has 3 columns and 3 rows
oper is *
second matrice has 3 columns and 2 rows
pi@raspberrypi:/tmp $ 

如果您没有 getline 替换

  char * lineptr = NULL;
  size_t n = 0;

  while (getline(&lineptr, &n, fp) > 0) {

例如,

  char * lineptr = malloc(1024);

  while (fgets(lineptr, 1024, fp) != NULL) {

答案 1 :(得分:0)

  

我不知道是否有一种方法可以存储矩阵的值   此时已经有了动态内存分配。

有,但要理解,即使您假设只有两个矩阵由单个运算符分隔,要真正动态读取未知数目的列和行的矩阵,还需要能够跟踪行数和行数。文件中遇到的每个矩阵的列,并在整个读取过程中特别注意分配和重新分配。

为简化此方法,您可以首先假设每行少于或等于512列(或一些适合您数据的合理数目)。这使您可以在必须为该行分配存储空间之前,将整数值的行读取到临时数组中。 (当然,您可以动态地分配和重新分配临时数组以达到这一点,但出于此处的目的,这只是添加了一组附加的条件检查和重新分配-在已经存在大量条件检查和重新分配的情况下。)

现在知道每行的列数(您将其存储在变量中以验证后续行的依据),就可以为该行分配存储空间(以及矩阵中的其余行,直到行以非数)

一种简化矩阵行和列存储以及将行和列数存储为单个变量的变量的方法是使用struct来保存矩阵及其大小。 (这使自己适合2个矩阵或任意数量的矩阵),这使您可以为要读取的任意数量的数组分配一个struct数组(或者您可以为自己的情况声明一个由2个矩阵组成的数组,并避免使用分配/重新分配检查)。例如:

typedef struct {
    int **a;
    size_t row, col;
} arr_t;

其中a是指向 pointer-to-pointer-to 的指针int,而rowcol保留其中的行数和列数分配并填充的a pointer-to-pointer 的选择允许以常规2D方式将本地索引编制为a[i][j],而不必将2D索引映射到单个偏移量中。 (您可以通过任何一种方式来做,选择权由您决定)

基本存储方案很简单,您可以分配(或静态声明)一定数量的struct,然后为a分配一些初始指针,并在读取并将每一行转换为您的临时数组,您可以为每一行分配存储空间,将临时行复制到该内存块,然后将该块的起始地址分配给下一个可用指针。当使用的指针数量等于您最初分配的指针数量时,您realloc个指针将继续运行。 (完成阵列操作后,请确保free()已分配的所有内容。

基本上就是这样。剩下的只是点您的i's并越过t's跟踪分配的内容以及何时需要重新分配。它并不复杂,只需要注意细节即可。以下实现为结构(数组数)以及数组本身分配(并将重新分配)。假设每一行都适合fgets()字节缓冲区(也可以根据需要进行分配和重新分配,但它与临时数组一样,它会使用1024字节缓冲区读取每一行,附加的分配/重新分配层具有出于示例目的而被省略)。

然后使用sscanf将每一行转换为整数,并将其转换为临时数组tmp(理想情况下,您将使用strtol来实现错误检测功能,但是为了简化示例而将其省略了) 。然后分配一个内存块,并将tmp中的整数复制到新的内存块中,并将其地址分配为当前数组中的下一个指针。 aidx用作结构索引数组(例如arr[0], arr[1], ...)当遇到非数字作为该行的第一个字符时,它将被当作数组之间的运算符,并将其存储在char的数组,数组索引aidx是递增的,下一个数组的填充将继续。

最后,将打印出阵列,并释放先前分配的所有内存。花一些时间来研究它,并了解每一点正在发生的事情以及原因。 (使用纸和铅笔跟踪迭代逻辑-通常比盯着计算机屏幕要好得多)

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

#define MINA    2   /* if you need a constant, #define one (or more) */
#define MAXC 1024

typedef struct {
    int **a;
    size_t row, col;
} arr_t;

int main (int argc, char **argv) {

    arr_t *arr;                     /* pointer to array type */
    size_t  aidx = 0, maxa = MINA,  /* arr index, max no. allocated */
            nrow = 0, ncol = 0,     /* current row/col count */
            maxrow = MINA, nop = 0; /* alloc'ed rows current array, no. op */
    char buf[MAXC],                 /* buffer to hold each line */
        op[MAXC];                   /* array to hold operators */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
    /* allocate/validate maxa no. of arr_t */
    if (!(arr = calloc (maxa, sizeof *arr))) {
        perror ("calloc-arr");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) { /* read each line info buf */
        int off = 0, n;         /* offset from start of line, n for "%n" */
        size_t tidx = 0;        /* temporary array index */
        char *p = buf;          /* pointer to advance in sscanf */
        int tmp[MAXC / 2 + 1];  /* temporary array, sized for max no. ints */

        if (!isdigit(*buf)) {   /* if 1st char non-digit, end of array */
            op[nop++] = *buf;   /* store operator */
            if (nrow)           /* if rows stored */
                arr[aidx++].row = nrow; /* set final number of rows */
            nrow = ncol = 0;    /* reset nrow/ncol counters */
            maxrow = MINA;      /* reset allocate rows */
            continue;           /* get next line of data */
        }
        if (aidx == maxa) {     /* check if no. of structs need realloc */
            void *atmp = realloc (arr, 2 * maxa * sizeof *arr);  /* realloc */
            if (!atmp) {        /* validate */
                perror ("realloc-arr");
                return 1;
            }
            arr = atmp;         /* assign new block to arr */
            /* set all new bytes zero (realloc doesn't initialize) */
            memset (arr + maxa, 0, maxa * sizeof *arr); 
            maxa *= 2;      /* update struct count */
        }

        /* read all integers in line into tmp array */
        while (sscanf (p + off, "%d%n", &tmp[tidx], &n) == 1) {
            off +=  n;
            tidx++;
        }
        if (tidx) { /* if integers stored in tmp */
            if (nrow == 0) {   /* if first row in array */
                /* allocate/validate maxrow pointers */
                if (!(arr[aidx].a = malloc (maxrow * sizeof *arr[aidx].a))) {
                    perror ("malloc-arr[aidx].a");
                    return 1;
                }
                arr[aidx].col = tidx;   /* fix no. cols on 1st row */                
            }
            else if (nrow == maxrow) {  /* realloc of row ptrs req'd? */
                void *atmp =            /* always realloc with temp ptr */
                    realloc (arr[aidx].a, 2 * maxrow * sizeof *arr[aidx].a);
                if (!atmp) {            /* validate every alloc/realloc */
                    perror ("realloc-arr[aidx].a");
                    return 1;
                }
                arr[aidx].a = atmp;     /* assign realloced block to ptr */
                maxrow *= 2;            /* update maxrow to current alloc */
            }
            if (tidx != arr[aidx].col) {    /* validate no. of columns */
                fprintf (stderr, "error: invalid number of columns "
                        "arr[%zu].a[%zu]\n", aidx, nrow);
                return 1;
            }
            if (!(arr[aidx].a[nrow] =   /* allocate storagre for integers */
                            malloc (tidx * sizeof *arr[aidx].a[nrow]))) {
                perror ("malloc-arr[aidx].a[nrow]");
                return 1;
            }
            /* copy integers from tmp to row, increment row count */
            memcpy (arr[aidx].a[nrow++], tmp, tidx * sizeof *tmp);
        }
    }
    if (nrow)   /* handle final array */
        arr[aidx++].row = nrow; /* set final number of rows */

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (size_t i = 0; i < aidx; i++) {     /* for each struct */
        printf ("\narray %zu:\n(%zu x %zu)\n",      /* output no. and size */
                i + 1, arr[i].row, arr[i].col);
        for (size_t j = 0; j < arr[i].row; j++) {   /* for each row */
            for (size_t k = 0; k < arr[i].col; k++) /* for each col */
                printf ("%4d", arr[i].a[j][k]);     /* output int */
            putchar ('\n');         /* tidy up with '\n' */
            free (arr[i].a[j]);     /* free row */
        }
        free (arr[i].a);    /* free pointers */
        if (i < nop)
            printf ("\noperator: '%c'\n", op[i]);
    }
    free (arr);     /* free structs */

    return 0;
}

示例输入文件

$ cat dat/unknown_arrays.txt
1 2 3
4 5 6
1 2 3
*
4 5 6
1 2 3

使用/输出示例

$ ./bin/read_unknown_arrays dat/unknown_arrays.txt

array 1:
(3 x 3)
   1   2   3
   4   5   6
   1   2   3

operator: '*'

array 2:
(2 x 3)
   4   5   6
   1   2   3

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,valgrind是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

$ valgrind ./bin/read_unknown_arrays dat/unknown_arrays.txt
==7714== Memcheck, a memory error detector
==7714== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==7714== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==7714== Command: ./bin/read_unknown_arrays dat/unknown_arrays.txt
==7714==

array 1:
(3 x 3)
   1   2   3
   4   5   6
   1   2   3

  operator: '*'

array 2:
(2 x 3)
   4   5   6
   1   2   3
==7714==
==7714== HEAP SUMMARY:
==7714==     in use at exit: 0 bytes in 0 blocks
==7714==   total heap usage: 10 allocs, 10 frees, 724 bytes allocated
==7714==
==7714== All heap blocks were freed -- no leaks are possible
==7714==
==7714== For counts of detected and suppressed errors, rerun with: -v
==7714== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误。

研究示例。要知道,为2x2数组还是150x150数组分配/重新分配都没关系,验证和重新分配检查是相同的,这使得像您这样的短输入文件看起来过于复杂。它们不是,只需要相同的代码即可处理2x2或150x150。如果您还有其他问题,请告诉我。