至于你正在读取的文件类型和你分配内存的方式,没有必要将整个文件读取两次:
.....*....................
...............*....*.....
..*.......*...............
............*.............
数据是面向行的,并且 --- 我想 --- 所有行都有相同的列数。数据将存储在
char **map;
一个指针数组,因此每个map[i] 是char* 并且可以保存一行数据。
fscanf() 是为使用表格数据而编写的,具有可能的分隔符和不同的字段类型,例如 csv 文件,其中包含许多 int 和 float 数据,例如由 # 分隔。使用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* 和平坦区域的情况下,分配与文件一样大的区域可能更简单,使用stat 或ftell 来获取文件大小而不是读取文件两次。
按行块分配内存
使用这种方式比较快,使用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;
}