【问题标题】:wrong output while reading file读取文件时输出错误
【发布时间】:2021-11-15 22:12:48
【问题描述】:

我正在尝试将文件的内容读入矩阵。由于我将有几个文件的行和列数量未知,因此我为矩阵动态分配了内存。

我的代码到现在为止。

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

int main(void)
{
    char** map, chr;
    int column = 0, row = 0, columns = 0, rows = 0, total_elements = 0;

    FILE* file = fopen("file.txt", "r+");

    // count numbers of rows and columns
    while (chr != EOF)
    {   
        if (chr == '\n')
        {
            rows = rows + 1;
        }
        chr = getc(file);
        total_elements+=1;
    }

    rows += 1;
    
    //Dividing the total number of elements by the number of rows to find the number of columns
    columns = (total_elements/rows) - 1;

    // alocate space for matrix
    map = (char **) malloc(rows * sizeof(char *));

    // allocating space for each column of each row
    for (row = 0; row < rows; row++) {
        map[row] = (char *) malloc(columns * sizeof(char));
    }

    // file reading
    for (row = 0; row < rows; row++) {
        for (column = 0; column < columns; column++) {
            if (!fscanf(file, "%c", &map[row][column]))
                break;
            printf("%c", map[row][column]);
        }
        printf("\n");
    }

    fclose(file);
    free(map);


    return 0;

这是文件:

....*.....................
..........................
........*........*........
.....*....................
...............*....*.....
..*.......*...............
............*.............
..........................
..............*...........
..................*.......
..*.......*......*........
....*..*..................
...**.....................
..........*...............
....................*.....
..........................
....**....................
......................*...

预期的输出将是文件的内容,但我得到的输出格式错误。

?Å É/Å @Q▓v            @Q
­?Å É/Å             xA¢v
­?Å É/Å  ÊÐ C Å └ Å µ
­?Å É/Å     
­?Å É/Å
­?Å É/Å         T(Å     P(
­?Å É/Å @Q▓v            @Q
­?Å É/Å             xA¢vx(
­?Å É/Å ╩  ╩ÈÐ  └ Å É&Å ug
­?Å É/Å ┼  ┼═Ð  └ Å É&Å ├
­?Å É/Å     
­?Å É/Å  H
­?Å É/Å 
­?Å É/Å 
­?Å É/Å 
░Å É/Å
ä
 H

我真的不知道我在哪里犯了这个错误。

【问题讨论】:

  • 首先:getc 返回 int 而不是 charchar 可能无法将有效值与 EOF 区分开来。
  • 首先char chr 有两个错误,应该是int chrwhile (chr != EOF) 在写入任何值之前对其进行检查。处理任何编译器警告。
  • @Gerhardh A char 几乎可以肯定能够持有 EOF,因为 EOF 通常为 -1。问题是 char 无法区分有效数据点和 EOF。
  • @WilliamPursell 你是对的。我用了不好的措辞。
  • 二读前必须rewind(file)。而且您不应该检查fscanf0,而是检查预期的转化次数(因为EOF 不是0)。

标签: c string file matrix dynamic-memory-allocation


【解决方案1】:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    char **map, chr;
    int column = 0, row = 0, columns = 0, rows = 0, total_elements = 0;

    FILE *file = fopen("file.txt", "r+");

    // count numbers of rows and columns

    while (1) {
        chr = (char) getc(file);

        if (chr == EOF)
            break;

        if (chr == '\n') {
            rows = rows + 1;
        } else {
            //\n is not part of matrix
            total_elements += 1;
        }

    }

    rows += 1;

    printf("%d,%d\n", total_elements, rows); //no need -1 since did not counted \n

//Dividing the total number of elements by the number of rows to find the number of columns
    columns = (total_elements / rows);

// alocate space for matrix
    map = (char **) malloc(rows * sizeof(char *));

// allocating space for each column of each row
    for (row = 0; row < rows; row++) {
        map[row] = (char *) malloc(columns * sizeof(char));
    }

//fix offset
    rewind(file);
// file reading
    for (row = 0; row < rows; row++) {
        for (column = 0; column < columns; column++) {
            chr = (char) getc(file);
            map[row][column] = chr;
        }
        chr = (char) getc(file); //read \n but not write to array
    }
    row = 0;
    for (row = 0; row < rows; row++) {
        for (column = 0; column < columns; column++) {
            printf("%c", map[row][column]);
        }
        printf("\n");
    }
    fclose(file);
    free(map);


    return 0;
}

【讨论】:

    【解决方案2】:

    至于你正在读取的文件类型和你分配内存的方式,没有必要将整个文件读取两次:

    .....*....................
    ...............*....*.....
    ..*.......*...............
    ............*.............
    

    数据是面向行的,并且 --- 我想 --- 所有行都有相同的列数。数据将存储在

        char **map;
    

    一个指针数组,因此每个map[i]char* 并且可以保存一行数据。

    fscanf() 是为使用表格数据而编写的,具有可能的分隔符和不同的字段类型,例如 csv 文件,其中包含许多 intfloat 数据,例如由 # 分隔。使用fscanf() 读取字符没有多大意义,您可以改用fgetc()fread()

    关于您的代码

    • 如前所述,您需要rewind 文件以再次读取数据
    • 另外,如上所述,您需要测试 '\n',因为它不是数据
    • 始终根据说明符的数量测试 fscanf() return

    考虑到我上面所说的,下面的循环会消耗你的文件

        rewind(file);
        row = 0;
        column = 0;
        while (1 == fscanf(file, "%c", &map[row][column]))
        {
            if (map[row][column] == '\n')
            {   row += 1, column = 0;
                printf("\n");
            }
            else
            {   printf("%c", map[row][column]);
                column += 1;
            }
        };  // while();
        fclose(file);
    

    一般来说,使用平面内存区域中的地图数据更易于管理,仅使用 char*,以 C 方式,并逐行存储地图,而不是使用 char**

    更灵活的方式

    使用char** 方式,您可以逐行读取数据并将地图保存为字符串,因为它可能对显示和使用有用,同时仍在使用

    map[][]
    

    供参考。

    我给你举个例子

    另外,既然你说你有很多文件大小,你应该考虑将文件名作为参数传递

    要获得更多控制权,您可以使用一些封装并将数据放在struct

    typedef struct
    {
        int    cols;
        int    rows;
        int    size;
        char** map;
    
    } Grid;
    

    这样你就可以拥有类似的功能

    int   show_grid(Grid*,const char*);
    

    然后写

        char title[100];
        sprintf(title, "\n    Map for %s\n",file_name);
        show_grid(&gr, title);
    

    在屏幕上查看

    
        Map for file.txt
    
    [18 rows, 26 columns]
    ....*.....................
    ..........................
    ........*........*........
    .....*....................
    ...............*....*.....
    ..*.......*...............
    ............*.............
    ..........................
    ..............*...........
    ..................*.......
    ..*.......*......*........
    ....*..*..................
    ...**.....................
    ..........*...............
    ....................*.....
    ..........................
    ....**....................
    ......................*...
    

    而且代码可以很简单,只有 4 行:

    void show_grid(Grid* g, const char* msg)
    {
        if (msg != NULL) printf("%s\n", msg);
        printf("[%d rows, %d columns]\n",
            g->rows, g->cols);
        for (int i = 0; i < g->rows; i+=1)
            printf("%s\n", g->map[i]);
        printf("\n");
    }
    

    确定列大小

        int ch = 0;
        for (gr.cols = 0; ch != '\n'; gr.cols += 1)
        {
            ch = fgetc(F);
            if (feof(F)) return -2;
        }
    

    您只需要找到第一行的结尾,因为所有行的大小相同,并且内存是按行分配的。

    在使用char* 和平坦区域的情况下,分配与文件一样大的区域可能更简单,使用statftell 来获取文件大小而不是读取文件两次。

    按行块分配内存

    使用这种方式比较快,使用fgets()读取数据每次调用消耗一整行。此外,由于数据不是平坦的,您可以将行保留为字符串以简化显示。见:

        // blocksize in lines
        int row = 0;
        while (!feof(F))
        {
            gr.map[row] = (char*)malloc(gr.cols);  // new row
            fgets(gr.map[row], gr.cols, F);
            gr.map[row][gr.cols - 2] = 0;
            row += 1;
            if (row == gr.size)
            {  // expand block
                int    new_size = gr.size + BLKSIZE;
                char** temp = (char**)realloc(gr.map, sizeof(char*)*new_size);
                if (temp == NULL) break;
                gr.map  = temp;
                gr.size = new_size;
            };
        };
        fclose(F);
    

    您可以有一个合适的BLKSIZE 来避免多次调整大小。

    在命令行传递文件名

    这样你可以有一个默认值,但也可以在命令行中传递文件名,以便它可以在脚本中使用。由于显示功能方便地显示文件名,您可以轻松地查看任意数量的文件,如

        // open file as 'F'
        const char* default_file_name = "file.txt";
        char        file_name[80];
        FILE*       F            = NULL;
        if (argc > 1) 
            strcpy(file_name, argv[1]);
        else
            strcpy(file_name, default_file_name);
    
        F = fopen(file_name, "r");
        if (F == NULL)
        {
            perror("Could not open file");
            return -1;
        }
    

    示例

    SO> .\f0-0922
    
        Map for file.txt
    
    [18 rows, 26 columns]
    ....*.....................
    ..........................
    ........*........*........
    .....*....................
    ...............*....*.....
    ..*.......*...............
    ............*.............
    ..........................
    ..............*...........
    ..................*.......
    ..*.......*......*........
    ....*..*..................
    ...**.....................
    ..........*...............
    ....................*.....
    ..........................
    ....**....................
    ......................*...
    
    SO> .\f0-0922 other.txt
    
        Map for other.txt
    
    [5 rows, 5 columns]
    ....*
    ...*.
    ..*..
    .*...
    *....
    

    考试代码

    #define BLKSIZE 20
    
    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct
    {
        int    cols;
        int    rows;
        int    size;
        char** map;
    
    } Grid;
    
    void  show_grid(Grid*,const char*);
    void  free_grid(Grid*);
    
    int main(int argc, char** argv)
    {
        // open file as 'F'
        const char* default_file_name = "file.txt";
        char        file_name[80];
        FILE*       F            = NULL;
        if (argc > 1) 
            strcpy(file_name, argv[1]);
        else
            strcpy(file_name, default_file_name);
    
        F = fopen(file_name, "r");
        if (F == NULL)
        {
            perror("Could not open file");
            return -1;
        }
    
        // define grid
        Grid gr = {0, 0, 0, NULL};
    
        // set 'cols' to column size
        int ch = 0;
        for (gr.cols = 0; ch != '\n'; gr.cols += 1)
        {
            ch = fgetc(F);
            if (feof(F)) return -2;
        }
        gr.cols = gr.cols + 1;  // add space to a terminating 0
        rewind(F);              // roll back 1st line
        gr.map = (char**)malloc((BLKSIZE * gr.cols) * sizeof(char*));  // initial size
        gr.size = BLKSIZE;
    
        // blocksize in lines
        int row = 0;
        while (!feof(F))
        {
            gr.map[row] = (char*)malloc(gr.cols);  // new row
            fgets(gr.map[row], gr.cols, F);
            gr.map[row][gr.cols - 2] = 0;
            row += 1;
            if (row == gr.size)
            {  // expand block
                int    new_size = gr.size + BLKSIZE;
                char** temp = (char**)realloc(gr.map, sizeof(char*)*new_size);
                if (temp == NULL) break;
                gr.map  = temp;
                gr.size = new_size;
            };
        };
        fclose(F);
    
        gr.rows = row;
        gr.cols -= 2;
    
        char title[100];
        sprintf(title, "\n    Map for %s\n",file_name);
        show_grid(&gr, title);
        free_grid(&gr);
    
        return 0;
    }
    
    void show_grid(Grid* g, const char* msg)
    {
        if (msg != NULL) printf("%s\n", msg);
        printf("[%d rows, %d columns]\n", g->rows, g->cols);
        for (int i = 0; i < g->rows; i += 1) printf("%s\n", g->map[i]);
        printf("\n");
    }
    
    void free_grid(Grid* g)
    {
        for (int i = 0; i < g->rows; i += 1) free(g->map[i]);
        free(g->map);
        g->map = NULL;
        return;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-15
      • 1970-01-01
      • 2018-12-14
      • 1970-01-01
      • 2020-08-25
      • 1970-01-01
      • 2015-03-17
      相关资源
      最近更新 更多