如何修复fscanf()的错误,检查C中的矩阵元素是否为整数

时间:2019-02-06 05:17:22

标签: c scanf

我的任务是从文本文件中读取矩阵,并找出它是否是一个魔方,我还必须确保矩阵的所有条目都是整数,如果不是,则必须写入标准输出错误消息,同时还描述了发生此错误的位置。

现在,我成功读取了矩阵的所有元素,但是验证是否为整数还是有问题。

我使用了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”时,它将仅针对a [1] [1]但会显示ERROR消息,但它将9替换为48。

当我有一个字符时(如本例所示),它将同时显示a [1] [1]('a')和a [1] [2]('9')的错误消息,并将其替换太。

我的问题是,为什么会发生这种情况?a [1] [1]和a [1] [2]之间到底发生了什么?如何解决?

编辑:我知道当我发现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 :(得分:2)

第一点:

while(!feof(file))
{

您将要查看Why is while ( !feof (file) ) always wrong?。此外,整个外部循环对于您的代码来说是多余的,可以轻松删除。

使用任何scanf系列函数,如果输入与格式字符串中使用的转换说明符不匹配,则匹配失败发生,从输入流中提取字符时会在失败时停止,导致失败的有问题字符留在输入流中(未读),只是在等待下一次尝试读取时咬住您。尝试将'a'读取为int类型为scanf的{​​{1}}会导致匹配失败

如何处理匹配失败?

由于您始终验证所有用户输入,因此,当返回值小于指定的转换次数(或scanf)时,就会检测到EOF的输入失败。每次在循环中使用fscanf读取一个整数,可以从行/列的角度准确地告诉您在读取方阵数据时发生输入故障的位置。知道发生故障的位置,您可以输出遇到无效数据的行/列。

要捕获无效数据以作报告之用,您可以简单地向前扫描fgetc一次读取一个字符,并用有问题的字符填充缓冲区,直到下一个有效的digit或{{1 }}(遇到数字的显式符号)(如果您想继续读取其他值,可以在此时使用+/-将有效数字(或符号)放回输入流中

简短示例

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; } 来分配{{1} }分配了指针和一个int整数块,并将每个分配的起始地址分配给每个指针,以允许将其作为2D数组进行索引。

示例输入文件

使用示例输入中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]

对代码进行注释以帮助您遵循。如果您有任何疑问,请告诉我。

答案 1 :(得分:1)

代码的关键问题:当fscanf遇到错误时,FILE指针不会向前移动。您需要使用fseek使其前进。

  

您需要研究的一些问题:

     
      
  • 如何分配足够大小的数组以正确读取所有输入
  •   
  • 基于文件的错误处理以及如何在使用fscanf读取时遇到错误时如何将FILE指针向前移动。 我的以下代码认为   仅存在单个不正确字符的特定情况,   如果有多个字符,则必须适当处理   礼物。
  •   
  • 您无需同时执行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

答案 2 :(得分:1)

另一个使用平面内存区域而不是指针的效率低下的“数组”的示例:

#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.