C 从文件中读取矩阵并检索其维数
我有一个文本文件,其中包含两个矩阵: 1 2 3 4 5 6 1 2 3 * 4 5 6 1 2 3] 我想能够读懂两个矩阵的维数和运算类型*+/-。我想同时检索维度和读取数据 在我的代码中,get_dim函数遍历文件中的数据以获得两个矩阵的维数。我不知道现在是否有办法通过动态内存分配来存储矩阵的值。通过函数read_matrix,我知道矩阵的维数,我再次读取相同的数据C 从文件中读取矩阵并检索其维数,c,file,matrix,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 <
#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);
}
关于你问题的第一个版本的一些评论 你的循环 一直读到文件的末尾,所以两个矩阵都需要检测*所以要分离第一个和第二个矩阵 如果不检测列数,则需要每行读取一行,然后至少在第一行计算每行的值数 在 您可以有一个未定义的行为,因为在只分配了6个字符的情况下,通过fgets str、24、fp读取最多24个字符 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;
}
如果没有getline替换
例如
char * lineptr = malloc(1024);
while (fgets(lineptr, 1024, fp) != NULL) {
我不知道是否有办法存储矩阵的值
此时已使用动态内存分配
有,但要知道,即使你假设只有两个矩阵由一个操作符分隔,要真正动态读取未知列数和行数的矩阵,需要能够跟踪文件中遇到的每个矩阵的行数和列数,并在整个读取过程中仔细注意分配和重新分配
为了简化该方法,您可以首先假设每行少于或等于512列,或者某个适合您的数据的合理数量。这允许您在为整数值行分配存储之前,将整数值行读入临时数组。当然,您可以动态地分配和重新分配临时数组以达到这一点,但出于这里的目的,这只是添加了一组附加的条件检查和重新分配,而这些条件检查和重新分配已经足够了
现在知道了每行的列数(保存在变量中以验证后续行),就可以为该行和矩阵中的其余部分分配存储,直到行以非数字开头
简化矩阵行和列的存储以及将行和列的数量存储为单一用途的变量的一种方法是使用结构来保存矩阵及其大小。这将有助于2个矩阵,或者任何您喜欢的数字。这允许您为要读取的任意数量的数组分配一个struct数组,或者您可以简单地为您的情况声明一个由2个矩阵组成的数组,并避免分配/重新分配检查。例如:
typedef struct {
int **a;
size_t row, col;
} arr_t;
其中a是指向int和row的指针,col保存分配和填充的a中的行数和列数。指针指向指针的选择允许以正常2D方式将本机索引作为[i][j],而无需将2D索引映射到单个偏移量。你可以选择任何一种方式,选择权在你
基本存储方案很简单,您可以分配或静态声明一定数量的struct,然后为a分配一些初始数量的指针,当您读取每一行并将其转换为临时数组时,您可以为每一行分配存储,将临时行复制到该内存块,并将该块的起始地址分配给下一个可用指针。当使用的指针数等于您最初分配的指针数时,您将重新定位更多指针并继续。使用完阵列后,请确保释放所有已分配的资源
基本上就是这样。其余的只是在跟踪分配的内容和需要重新分配的时间上点i和交叉t。这并不复杂,只是需要仔细注意细节。以下实现分配并将重新分配数组的结构数以及数组本身。它使用FGET读取每一行,假设每一行都适合1024字节的缓冲区,该缓冲区也可以根据需要进行分配和重新分配,但与临时数组一样,出于示例目的,省略了额外的分配/重新分配层
然后使用sscanf将每一行转换为整数,转换为临时数组tmp。为了检测错误,您最好使用strtol,但为了简化示例,省略了这一点。然后分配一块内存并
tmp中的整数被复制到新的内存块中,其地址被指定为当前数组中的下一个指针。aidx用作您的结构索引数组,例如arr[0]、arr[1]、。。。当遇到一个非数字作为行中的第一个字符时,它将作为数组之间的运算符,并存储在字符数组中,数组索引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;
}
示例使用/输出
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:1始终保留指向内存块起始地址的指针,以便在不再需要时释放内存块
必须使用内存错误检查程序,以确保您不会试图访问内存或写入超出/超出分配的块的边界,尝试在未初始化的值上读取或建立条件跳转,最后确认释放所有已分配的内存
对于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。如果您还有其他问题,请告诉我。@MichaelangeloMeucci什么?从什么时候开始6==24?注:根据定义,sizeofchar是1char c;==>INTC;我编辑了我的答案,添加了一个关于代码执行工作的建议。请不要在阅读我们的评论时更改/更正您的问题,这会使所有内容无法阅读/无法管理。考虑到我们的回答,我投票结束这个问题,因为OP不断地编辑这个问题,这并不是因为它不清楚,而是改变它,删除你的评论中的错误,并放置一个完全不同的代码版本。我们不是在一个问题前面,而是在几个不同的问题前面,这取决于我们阅读的时间。即使我要求停止,OP仍在继续改变。不可能做到这一点,因为这是一段伟大的代码。我有一些疑问1你为什么用指针指向指针2我不知道检索所有数据然后分配内存是更好的编程策略,就像我更新的代码中存储所有值所需的那样,还是在数据读取时分配和重新分配你的选项是指针指向指针,或者,您必须操纵1D数组索引,以作为大多数矩阵例程使用的2D数组进行访问。每行额外使用1个指针可以忽略不计,因为您只是在重新分配指针,所以可以最大限度地减小重新分配的大小。最佳情况下,当used==可用指针时,您将分配指针块和realloc。malloc/realloc是相对昂贵的操作,通过分配块,您可以最小化所需的调用数量。在这里,您只需一次分配一个结构。
char * lineptr = malloc(1024);
while (fgets(lineptr, 1024, fp) != NULL) {
typedef struct {
int **a;
size_t row, col;
} arr_t;
#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
$ 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)