读取文件时输出错误

wrong output while reading file

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

到目前为止我的代码。

#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

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

#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;
}

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

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

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

    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;
}