чтение матриц из файла и получение их размеров - PullRequest
0 голосов
/ 26 мая 2019

У меня есть текстовый файл, содержащий две матрицы в этой форме:

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 ]

1 голос
/ 26 мая 2019

Несколько замечаний по первой версии вашего вопроса

Ваша петля

 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 ) {

вы можете иметь неопределенное поведение, потому что вы читаете до 24 символов, делая fgets (str, 24, fp), в то время как вы выделяете только 6

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) {
0 голосов
/ 27 мая 2019

Я не знаю, есть ли способ сохранить значения матриц уже в этот момент с динамическим распределением памяти.

Есть, но поймите, что даже если вы предполагаете, что есть только две матрицы, разделенные одним оператором, чтобы действительно выполнять динамическое чтение матриц с неизвестным числом столбцов, а строки могут отслеживать количество строк и столбцы для каждой матрицы, встречающейся в файле, и особое внимание к распределению и перераспределению на протяжении всего процесса чтения.

Чтобы упростить подход, вы можете сначала предположить, что в строке будет меньше или равно 512 столбцов (или какое-то разумное число, соответствующее вашим данным). Это позволяет вам прочитать строку целочисленных значений во временный массив, прежде чем выделять хранилище для строки. (Конечно, вы можете динамически выделять и перераспределять временный массив, чтобы добраться до этой точки, но для целей здесь просто добавляется дополнительный набор условных проверок и перераспределения - там, где их уже много).

Теперь, зная количество столбцов на строку (которое вы сохраняете в переменной для проверки последующих строк), вы можете выделить хранилище для этой строки (и остатка в этой матрице, пока строка не начнется с цифра)

Один из способов упростить хранение строк и столбцов матрицы вместе с переменными, которые хранят количество строк и столбцов как единое целое, заключается в использовании struct для хранения матрицы и ее размера. (это позволяет использовать 2 матрицы или любое другое число, которое вам нравится). Это позволяет вам выделить массив структуры для любого числа массивов, которые нужно прочитать (или вы можете просто объявить массив из 2 для вашего случая и избежать проверка распределения / перераспределения). Например:

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

Где a - это указатель на указатель на int, а row и col содержат количество строк и столбцов в выделенном и заполненном a. Выбор указатель-на-указатель позволяет выполнять внутреннюю индексацию как a[i][j] в обычном двухмерном режиме без необходимости отображать двумерную индексацию в одно смещение. (вы можете сделать это любым способом, выбор за вами)

Базовая схема хранения проста: вы выделяете (или статически объявляете) некоторое число 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.Дайте мне знать, если у вас есть дополнительные вопросы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...