Как исправить ошибку fscanf (), проверяя, является ли элемент в матрице целым или нет в C - PullRequest
0 голосов
/ 06 февраля 2019

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

Теперь мне удалось прочитать все элементы матрицы, но у меня возникла проблема с проверкой, является ли она целым числом или нет.

Я использовал fscanf (), потому что я знаю, что в случае успеха он возвращает количество элементов, которые он прочитал успешно, и возвращает EOF или 0 в противном случае.

Вот матрица:

3
12 3 87
78 a 9
45 0 23

Первый элемент - это количество строк и столбцов.

, и вот мой код (часть, в которой мне нужна помощь, по крайней мере)

while(!feof(file))
{
    while ( fgets(buffer, sizeof(buffer), file) )
    {

        for ( int j = 0; j < n; j++ )
        {
            if ( i == n )
            {
                break;
            }

            m = fscanf(file, "%d", &a[i][j]);
            printf("\n%d", m);

            if ( m == 0 || m == EOF )
            {
                printf("\nERROR, %d, %d, %d \n", i, j, a[i][j]);
            }

        }
        i++; 

    }



}

, и вот мой вывод (прямо сейчас я 'Я просто пытаюсь выяснить эту проблему, так что это не так, как это будет выглядеть позже):

1
1
1
1
0
ERROR, 1, 1, 0

0
ERROR, 1, 2, 48

1
1
1
12 3 87
78 0 48
45 0 23

Когда я заменяю 'a' на число типа (с плавающей точкой), например, он будет отображатьСообщение ERROR только для [1] [1] НО заменит 9 на 48.

Когда у меня есть символ (как в этом случае), он отобразит сообщение ERROR для [1] [1] ('a') и a [1] [2] ('9') и замените их тоже.

И мой вопрос: почему это происходит и что, черт возьми, происходит между [1] [1] и [1] [2] и как это исправить.

РЕДАКТИРОВАТЬ: IЯ знаю, что могу просто выйти из программы, когда найду 1 случай, но мне очень интересно, почему возникает эта ошибка.

fscanf(file, "%d", &n );//I read the first integer, which is also the maximum  capacity of my 2d array

int a[n][n];

Ответы [ 3 ]

0 голосов
/ 06 февраля 2019

Первые баллы:

while(!feof(file))
{

Вы захотите посмотреть на Почему while (! Feof (file)) всегда неверно? .Кроме того, весь внешний цикл является излишним для вашего кода и может быть просто удален.

С любой из функций семейства scanf, когда ввод не соответствует спецификатору преобразования , используемому встрока формата , ошибка соответствия происходит, извлечение символа из входного потока останавливается в точке сбоя, вызывающие сбой символы, вызывающие ошибку, остаются во входном потоке (непрочитанные) простожду, чтобы укусить вас при следующей попытке прочитать.Попытка прочитать 'a' как тип int с scanf приводит к ошибке соответствия .

Как обработать ошибку соответствия?

Поскольку вы всегда проверяете весь пользовательский ввод, как вы заметили, вы обнаруживаете ошибку ввода с помощью scanf, когда возврат меньше указанного числа преобразований (или EOF).Чтение целого числа за раз с fscanf в ваших циклах говорит вам точно, где произошел сбой ввода с точки зрения строки / столбца при чтении данных с квадратной матрицей.Зная, где произошел сбой, вы можете вывести строку / столбец, в котором были обнаружены недопустимые данные.

Чтобы зафиксировать недопустимые данные в целях составления отчетов, вы можете просто выполнить сканирование вперед с fgetc, читая символ за раззаполнение буфера ошибочными символами до тех пор, пока не встретятся следующие действительные digit или +/- (явный знак для цифры) (в этот момент вы можете использовать ungetc, чтобы вернуть действительную цифру (или знак) обратно на входпоток, если вы хотите продолжить чтение дополнительных значений)

Краткий пример

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

#define MAXC 1024

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

    int **m = NULL;
    unsigned dim = 0;
    /* 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;
    }

    if (fscanf (fp, "%u", &dim) != 1) { /* read square mtrx dim */
        fputs ("error: invalid format, no dimension.\n", stderr);
        return 1;
    }

    if (!(m = malloc (dim * sizeof *m))) {  /* allocate/validate dim ptrs */
        perror ("malloc-m");
        return 1;
    }

    for (unsigned i = 0; i < dim; i++)   /* allocate dim rows of dim int */
        if (!(m[i] = calloc (dim, sizeof *m[i]))) { /* zero mem w/calloc */
            perror ("calloc-m[i]");
            return 1;
        }

    for (unsigned i = 0; i < dim; i++)              /* for each row */
        for (unsigned j = 0; j < dim; j++) {        /* for each col */
            if (fscanf (fp, "%d", &m[i][j]) != 1) { /* read/validate int */
                char buf[MAXC], *p = buf;   /* buf and ptr to buf */
                int c;                      /* int for char */
                /* read while !EOF, not digit and not -/+ */
                while ((c = fgetc(fp)) != EOF && (c < '0' || '9' < c) &&
                        c != '-' && c != '+')
                    if (!isspace(c))    /* if not a space */
                        *p++ = c;       /* store offending char(s) */
                *p = 0;                 /* nul-terminate buf */
                if (c != EOF)           /* if c a char - put it back */
                    ungetc(c, fp);
                /* output location of invalid input */
                printf ("error: m[%d][%d] - invalid entry '%s'.\n",
                        i, j, buf);
                return 1;   /* and bail - or continue -- your choice */
            }
        }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (unsigned i = 0; i < dim; i++) {    /* for each row */
        for (unsigned j = 0; j < dim; j++)  /* for each col */
            printf (" %3d", m[i][j]);       /* output value */
        putchar ('\n');                     /* output newline */
        free (m[i]);                        /* free ints in row */
    }
    free (m);   /* free pointers */

    return 0;
}

( примечание: вы не сказали, как вывыделенное хранилище, поэтому простой указатель на указатель на int был использован для выделения dim указателей, и был выделен блок из dim целых чисел, а начальный адрес для каждого выделения назначен каждому из указателейчтобы разрешить индексирование в виде двумерного массива)

Примеры входных файлов

Использование примера ввода с недопустимым int при [1][1]:

$ cat ~/tmpd/mtrx.txt
3
12 3 87
78 a 9
45 0 23

Пример с хорошими значениями:

$ cat ~/tmpd/mtrxgood.txt
3
12 3 87
78 8 9
45 0 23

Пример Использование / Вывод

С недопустимым int при [1][1]:

Программа корректно сообщает о местоположении и обидном символе:

$ ./bin/readsquaremtrx ~/tmpd/mtrx.txt
error: m[1][1] - invalid entry 'a'.

При правильных значениях все значения считываются и матрица выводится на экран до освобождения всей выделенной памяти:

$ ./bin/readsquaremtrx ~/tmpd/mtrxgood.txt
  12   3  87
  78   8   9
  45   0  23

Код прокомментирован, чтобы помочь вам следовать.Если у вас есть какие-либо вопросы, просто дайте мне знать.

0 голосов
/ 06 февраля 2019

Другой пример использования плоской области памяти вместо немного неэффективного «массива» указателей на указатели:

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

int is_magic_square(int *square, size_t dimension)
{
    if(dimension < 3) {
        return 0;
    }

    int prev_row_sum;
    int prev_col_sum;
    int d1_sum = 0;
    int d2_sum = 0;

    for (size_t y = 0; y < dimension; ++y) {
        int curr_row_sum = 0;
        int curr_col_sum = 0;
        for (size_t x = 0; x < dimension; ++x) {
            curr_row_sum += square[y * dimension + x];
            curr_col_sum += square[x * dimension + y];

            if (x == y) {
                d1_sum += square[y * dimension + x];
                d2_sum += square[y * dimension + dimension - 1 - x];
            }
        }

        if (y && (curr_row_sum != prev_row_sum || curr_col_sum != prev_col_sum)) {
            return 0;
        }

        prev_row_sum = curr_row_sum;
        prev_col_sum = curr_col_sum;
    }

    return prev_row_sum == d1_sum && prev_row_sum == d2_sum ? prev_row_sum : 0;
}

int main(void)
{
    char const *filename = "test.txt";
    FILE *input = fopen(filename, "r");
    if (!input) {
        fprintf(stderr, "Couldn't open \"%s\" for reading :(\n\n", filename);
        return EXIT_FAILURE;
    }

    size_t dimension;
    if (fscanf(input, " %zu", &dimension) != 1) {
        fprintf(stderr, "Faild to read squares dimensions from \"%s\" :(\n\n", filename);
        fclose(input);
        return EXIT_FAILURE;
    }

    int result = EXIT_FAILURE;
    printf("Reading a %zu x %zu square from \"%s\" ...\n\n", dimension, dimension, filename);

    int *square = calloc(dimension * dimension, sizeof(*square));
    if (!square) {
        fputs("Not enough memory :(\n\n", stderr);
        goto cleanup;
    }

    for (size_t y = 0; y < dimension; ++y, putchar('\n')) {
        for (size_t x = 0; x < dimension; ++x) {
            int value;
            if (fscanf(input, " %d", &value) != 1 || value < 1) {
                fprintf(stderr, "\n\nFailed to read value at (%zu, %zu) from \"%s\" :(\n\n",
                        x + 1, y + 1, filename);
                goto cleanup;
            }

            for (size_t pos = 0; pos < y * dimension + x; ++pos) {
                if (square[pos] == value) {
                    fprintf(stderr, "\n\nDuplicate value %d found at (%zu, %zu) in \"%s\" :(\n\n",
                            value, x + 1, y + 1, filename);
                    goto cleanup;
                }
            }

            if(value > dimension * dimension) {
                fprintf(stderr, "\n\nValue %d at (%zu, %zu) in \"%s\" is out of range 1, 2, ..., %zu^2 :(\n",
                        value, x + 1, y + 1, filename, dimension);
                goto cleanup;
            }

            printf("%4d ", value);
            square[y * dimension + x] = value;
        }
    }

    int sum = is_magic_square(square, dimension);
    printf("\nThis %s a perfect square!\n", sum ? "is" : "is not");

    if (sum) {
        printf("It's sum is %d.\n", sum);
    }

    putchar('\n');
    result = EXIT_SUCCESS;

cleanup:
    free(square);
    fclose(input);
    return result;
}

Пример ввода:

4
10    3   13    8
 5   16    2   11
 4    9    7   14
15    6   12    1

Пример вывода:

Reading a 4 x 4 square from "test.txt" ...

  10    3   13    8
   5   16    2   11
   4    9    7   14
  15    6   12    1

This is a perfect square!
It's sum is 34.
0 голосов
/ 06 февраля 2019

Ключевая проблема вашего кода : ваш указатель FILE не двигается вперед, когда fscanf обнаруживает ошибку.Вы должны продвинуть его вперед, используя fseek.

Некоторые проблемы, на которые вам нужно обратить внимание:

  • Как выделить массив достаточного размера, чтобы правильно прочитать все входные данные
  • Обработка ошибок на основе файлов и способ перемещения указателя FILE вперед, если он обнаруживает ошибку при чтении с использованием fscanf. В приведенном ниже коде рассматривается только конкретный случай присутствия одного неправильного символа, вам придется обрабатывать его соответствующим образом, если присутствует более одного символа.
  • Вам не нужносделать оба fgets и fscanf.Достаточно только одного.
  • Посмотрите, если вы действительно хотите использовать fscanf.Та же логика может быть достигнута с использованием других методов чтения, связанных с FILE.

Обрабатывайте приведенный ниже код строго как псевдокод , просто давая некоторые указания.Это не обрабатывает все условия вообще.Вам нужно работать над многими аспектами вашего кода и обрабатывать дополнительные условия ошибок и т. Д. Я только что дал указатель на то, как вы можете решить эту проблему.

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

int main(void)
{

  FILE *f = fopen("./check.txt", "r");
  int a[3][3] = {0};
  int n, res, i;

  res = fscanf(f, "%d", &n);

  i = 0;
  while(i<n)
  {
    for(int j = 0; j < n; j++)
    {
       int val = 0;
       res = fscanf(f, "%d", &val);
       if ((res == 0) || (res == EOF))
       {
           printf("Error: res = %d for i = %d, j = %d, val = %d\n", res, i, j, val);
           fseek(f, 1, SEEK_CUR);
       }
       else
           a[i][j] = val;
    }
    i++;

  }

  printf("The Matrix\n");
  for(int i = 0; i<n; i++)
  {
    for (int j=0; j<n; j++)
        printf("%d ", a[i][j]);
    printf("\n");
  }

}

Вывод:

Error: res = 0 for i = 1, j = 1, val = 0
The Matrix
12 3 87
78 0 9
45 0 23
...