从 C 文件中读取二维数组

Reading a 2D array from a file in C

我正在尝试创建一个程序来从文件中读取二维数组,然后将其打印出来。

文件设置为第一行是行数,然后是列数。之后绘制数组。举例如下:

3 5
10.4 15.1 18.5 13.3 20.8
76.5 55.3 94.0 48.5 60.3
2.4 4.6 3.5 4.6 8.9

我的问题是我只知道如何使用 fgets 和 sscanf 读取每行的第一个元素,因此忽略了后面的数字。

#include <stdio.h>
#include <string.h>

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

FILE* f = fopen("plottestdata", "r");
char size[20];
int height, width,ii=0,cc,jj,kk;
float array[100][100];
char horiz[500];

if(fgets(size, 20, f)!= NULL){

    sscanf(size,"%d %d", &height, &width);
    printf("%d %d\n",height, width);
}
while(fgets(horiz, 500, f)!=NULL)
{

    if(ii<height)
    {
        for(cc=0;cc<width;cc++)
        {
        sscanf(horiz, "%f", &array[ii][cc]);
        }
    ii++;
    }
}
for(jj=0;jj<width;jj++)
    {
        for(kk=0;kk<height;kk++)
        {
        printf("%f ", array[jj][kk]);
        }
    }
fclose(f);
return 0;
}

这为我提供了重复多次的每一行的第一个元素的输出,我理解为什么但不确定如何修复它。它正在读取的文件实际上是一个 20x20 数组,尽管其格式与示例相同。

此外,为了缩短一个长问题,我省略了错误检查。

为什么不从 stdin 中读取,而不是从具有硬编码文件名的文件中读取?您可以使用 fgets(size, sizeof size, stdin) 来读取带有尺寸的第一行。

然后用scanf(" %f", &array[ii][cc)一个一个读入值。

一定要测试是否scanf returns 1,如果不是则添加一些错误处理。

我忍不住调整和简化你的代码,它是如此接近,避免使用 fgets 并使用 fscanf 直接读取值。加上基本的错误检查。

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

#define MWIDTH  100
#define MHEIGHT 100

int main(void){

    FILE* f;
    int height, width, ii, jj;
    float array[MHEIGHT][MWIDTH];

    if((f = fopen("plottestdata.txt", "r")) == NULL)
        exit(1);

    if(fscanf(f, "%d%d", &height, &width) != 2)
        exit(1);
    if (height < 1 || height > MHEIGHT || width < 1 || width > MWIDTH)
        exit(1);

    for(jj=0; jj<height; jj++)
        for(ii=0; ii<width; ii++)
            if(fscanf(f, "%f", &array[jj][ii]) != 1)
                exit(1);
    fclose(f);

    for(jj=0; jj<height; jj++){
        for(ii=0; ii<width; ii++)
            printf ("%10.1f", array[jj][ii]);
        printf("\n");
    }
    return 0;
}

程序输出:

  10.4      15.1      18.5      13.3      20.8
  76.5      55.3      94.0      48.5      60.3
   2.4       4.6       3.5       4.6       8.9

正如您已经发现的那样,您需要在扫描上一个值后从中断处扫描下一个值。不幸的是,sscanf 没有流的属性;它不会记住它的最后位置,如果你传递相同的字符串,它总是重新开始。

幸运的是,还有很多其他方法可以实现顺序扫描。

Tokenisation 你可以首先标记你的行,然后将每个标记解析为浮点数。 string.h>strtok<stdlib.h>strtod 将为您完成这项工作。

        char *token = strtok(horiz, " \t\n");

        for (cc = 0; cc < width; cc++) {
            if (token) {                
                array[ii][cc] = strtod(token, NULL);
                token = strtok(NULL, " \t\n");
            } else {
                array[ii][cc] = 0.0;
            }
        }

跟踪尾部 I strtod 还有一个特点:当您提供一个指向 char 指针的指针时,它会告诉您解析停止的确切位置。 strtod 也跳过前导白色 space。您可以使用这两个事实来解析由白色 space.

分隔的浮点数
        char *p = horiz;

        for (cc = 0; cc < width; cc++) {
            char *tail;

            array[ii][cc] = strtod(p, &tail);
            p = tail;
        }

记尾II sscanf有一种特殊的格式,告诉你在整数的某个点解析了多少个字符。上面可以写成 sscanf:

       char *p = horiz;

        for (cc = 0; cc < width; cc++) {
            int len;

            sscanf(p, "%f%n", &array[ii][cc], &len);
            p += len;
        }

所有这些都适用于您的示例,但其中 none 会进行适当的错误检查。第一个示例至少半心半意地尝试捕捉一行的标记少于矩阵的列的情况。

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]){
    FILE* f = fopen("plottestdata", "r");
    char size[20];
    int height, width,ii=0,cc,jj,kk;
    char horiz[500];

    if(fgets(size, 500, f)!= NULL){

    sscanf(size,"%d %d", &height, &width);
    printf("%d %d\n",height, width);
    }
    float array[height][width];
    while(fgets(horiz, 500, f)!=NULL)
    {

    if(ii<height)
    {
         sscanf(horiz, "%f %f %f %f %f", &array[ii][0],&array[ii][1],
                    &array[ii][2],
                    &array[ii][3],
                    &array[ii][4]);       
              ii++;
        }
    }
    for(ii=0;ii<height;ii++)
    {
        for(kk=0;kk<width;kk++)
        {
            printf("%.2f ", array[ii][kk]);
        }
        printf("\n");
    }
    fclose(f);
    return 0;
}