【问题标题】:reading matrices from a file and retriving their dimension从文件中读取矩阵并检索它们的维度
【发布时间】:2019-05-26 09:49:29
【问题描述】:

我有一个包含两个矩阵的文本文件:

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

【问题讨论】:

  • char c; ==> int c;
  • 我编辑了我的答案以添加一份代码提案
  • 请不要在阅读我们的评论时更改/更正您的问题,这会使所有内容无法阅读/无法管理。只需考虑我们的答案
  • 我投票决定将此问题作为题外话结束,因为 OP 不断编辑问题,这不是因为不清楚,而是要更改它以消除查看您的评论和还要放一个完全不同版本的代码。我们不是面对一个问题,而是面对几个不同的问题,具体取决于我们阅读的时间。即使我要求停止,OP 也会继续进行更改。这是无法管理的

标签: c file matrix


【解决方案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 ) {

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

c != EOF 要求 cint,而不是 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) {

【讨论】:

  • @MichaelangeloMeucci 什么?从什么时候 6 == 24 开始?另请注意 definition sizeof(char) 是 1
【解决方案2】:

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

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

为了简化方法,您可以首先假设每行的列数少于或等于 512 列(或适合您数据的合理数字)。这允许您在必须为该行分配存储空间之前将一行整数值读入临时数组。 (您当然可以动态分配和重新分配临时数组以达到该目的,但出于此处的目的,这只是添加了一组额外的条件检查和重新分配——已经有很多了)。

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

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

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

其中a 是一个指向指针的指针 introwcol 保存分配和填充的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], ...)当非数字作为行中的第一个字符遇到时,它被视为数组之间的运算符,并存储在@的数组中987654343@,数组索引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。如果您还有其他问题,请告诉我。

【讨论】:

  • 这是一段很棒的代码。我有一些疑问 1)为什么要使用指向指针的指针 2)我不知道检索所有数据然后分配存储所有值所需的内存(如在我更新的代码中)是否是更好的编程策略或在读取数据时分配和重新分配
  • 您的选项是指针对指针,或者您必须操作一维数组索引才能作为二维数组访问(大多数矩阵例程使用)。每行 1 个指针的额外使用可以忽略不计,并且提供了最小化重新分配大小的好处,因为您只是重新分配指针。理想情况下,您将分配指针块,并且仅在 used == available 指针时分配 reallocmalloc/realloc 是相对昂贵的操作,通过分配块可以最大限度地减少所需的调用次数。在这里,您只需一次分配一个结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 2021-07-10
  • 1970-01-01
  • 2016-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多