将迷宫存储到 2D 数组中

  • 本文关键字:数组 2D 迷宫 存储 c
  • 更新时间 :
  • 英文 :


我正在尝试将以下迷宫从作为命令行参数给出的文件编码为 2d 矩阵:

################
#              #
###########    #
#              #
## # # #  ######
#              #
#              #
#              #
################
#include <stdio.h>
#include <stdlib.h>
#define MAX 300
int countLines(FILE *fp);
int countColumns(FILE *fp);
void storeMaze(int N, int M, int matrix[N][M], FILE *fp);

int main(int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "Invalid format.nCorrect format: %s maze_file.datn", argv[0]);
exit(EXIT_FAILURE);
}
FILE *fin = fopen(argv[1], "rb");
if(!fin)
{
fprintf(stderr, "Couldn't open file: %sn", argv[1]);
exit(EXIT_FAILURE);
}
int N = countLines(fin); // lines in the matrix
rewind(fin);
int  M = countColumns(fin); // columns in the matrix
rewind(fin);
//  printf("Lines: %d | Columns: %dn", N, M);
int maze[N][M];
storeMaze(N, M, maze, fin);
fclose(fin);
return 0;
}
int countLines(FILE *fp)
{
char c;
int count = 0;
while((c = fgetc(fp)) != EOF)
{
if (c == 'n') // Increment count if this character is newline
{
count++;
}
}
return count;
}
int countColumns(FILE *fp)
{
char c;
int count = 0;
while((c = fgetc(fp)) != EOF)
{
if (c == 'n') // Increment count if this character is newline
{
break;
}
count++;
}
return count;
}
void storeMaze(int N, int M, int matrix[N][M], FILE *fp)
{
int i, j, startPoint, endPoint;
char buf[MAX];
for(i = 0; i < N; i++)
{
for(j = 0; j < M; j++)
{
while(fgets(buf, sizeof buf, fp))
{
if(buf[j] == '#')
{
matrix[i][j] = 0;
}
if(buf[j] == ' ')
{
matrix[i][j] = 1;
}
}
printf("%d ", matrix[i][j]);
}
}
putchar('n');
}

根据文件中的每个字符,输出中应该只有 1 和 0,其中一些打印正确,但大多数是垃圾值。直到现在我还没有真正使用矩阵,所以我猜也许我在商店迷宫或其他东西中传递了错误的参数。有什么想法吗?

您有许多问题,其中最重要的是尝试使用fgets()在每个字符位置读取一行输入(通常,这是一次读取一的正确工具,但是,在您使用它的地方,您应该一次读取一个字符

应合并countLines()countColumns(),因为可以在一次传递文件中确定列数和行数。只需将NM的地址作为参数传递,一旦在函数中确定了行数和列数,就分别更新NM持有的地址处的值。

确定行数和列数时,必须验证文件中的每一行是否具有相同的列数。如果没有,则该文件没有适合您的迷宫的有效格式。

在确定文件中的行数时,必须处理文件包含非 POSIX eof 的情况。(文件中的最终文本之后没有'n')。当到达文件末尾时,您可以通过检查前一个字符是否是'n'字符来执行此操作。如果不是,则需要将行数增加1,以解释最后一行后不跟'n'的事实。

使用fgetc()逐字符读取文件是可以的。I/O 子系统为从文件系统读取的数据提供了一个读取缓冲区,而读取fgetc()只是从该缓冲区读取(您不会对每个字符执行重复的磁盘读取)。在 Linux 和 Windows 上,缓冲区为BUFSZ字节(Linux 上为8192,Windows 上为512字节)。

确定行和列的一种简单方法是使用fgetc()循环连续读取,并在读取文件时跟踪行和列计数。通过跟踪last迭代中的字符,可以确定文件是否作为文件中最后一行数据(POSIX 文件末尾)之后的'n',否则,请将1添加到行计数中。

您可以按如下方式编写组合countRowsCols()函数。代码被注释,以便您可以遵循跟踪您在文件中的位置的逻辑,以及如何根据第一行列计数设置列数,然后验证每个后续行具有相同的列数,例如

/* determine number of rows and columns in fp, and
* validate each row has the same number of columns.
* returns 0 on failure, 1 otherwise, the values at
* the addresses for rows and cols are updated to
* hold the number of rows and columns in fp.
*/
int countRowsCols (FILE *fp, int *rows, int *cols)
{
int row = 0,            /* row counter */
col = 0,            /* column counter  */
last = 0;           /* hold char read during last loop iteration */

*rows = *cols = 0;      /* zero value at pointer addresses */

for (;;) {                                  /* loop continually */
int c = fgetc (fp);                     /* read character */
if (c == EOF) {                         /* if EOF */
if (last != 'n') {                 /* if last not n, non-POSIX eof */
row++;                          /* add +1 to row */
if (*cols && col != *cols) {    /* if *cols set and col != *cols */
fputs ("error: unequal number of columns in file.n", stderr);
return 0;
}
}
break;                  /* break on EOF */
}
else if (c == 'n') {       /* if char is 'n' */
if (!*cols)             /* if *cols not set */
*cols = col;        /* set no. of cols based on 1st row */
if (*cols != col) {     /* check col against *cols for remaining lines */
fputs ("error: unequal number of columns in file.n", stderr);
return 0;
}
col = 0;                /* reset column count */
row++;                  /* increment rows */
}
else                        /* normal character */
col++;                  /* increment col count */
last = c;                   /* set last = c */
}

return *rows = row;     /* return result of comparison validating all rows read */
}

确定行数和列数后,storeMaze()中的读取循环的功能大致相同。一次读取一个字符,当达到列计数时,检查下一个字符是否'n'并重置下一行的列计数,并随时验证行计数。到达末尾时,验证是否已读取预期的列字符行行数。你可以这样写它:

/* fill VLA (NxM) from fp setting element 1 if character is '#'
* or 0 for all others. validate n follows each row, optional
* on last line. returns 1 on success, 0 otherwise.
*/
int storeMaze (int N, int M, int (*matrix)[M], FILE *fp)
{
int row = 0;            /* row counter */

while (row < N) {
int c,              /* char to read */
col = 0;        /* column counter */
do {                /* loop M times */
if ((c = fgetc (fp)) == 'n' || c == EOF) {   /* read c, check EOL and EOF */
fputs ("error: premature EOL or EOF.n", stderr);
return 0;
}
matrix[row][col++] = (c == '#') ? 1 : 0;      /* set value, increment col */
} while (col < M);

if ((c = fgetc (fp)) != 'n' && c != EOF) {       /* validate next char is n */
fputs ("error: invalid column count.n", stderr);
return 0;
}
row++;              /* increment row */
}

return row == N;        /* return result of comparison validating all rows read */
}

(注意:返回类型从void更改为int,以便能够将storeMaze()函数的成功或失败传达回调用方。对于对代码的持续操作至关重要的任何函数,必须验证该函数是成功还是失败。为函数选择void类型会抛弃将成功或失败传达回调用函数的主要方法(此处main())。

总而言之,您可以执行以下操作。请注意,用值填充maze后,maze中的值将用于生成应与原始数据文件匹配的输出。这是您必须确认程序逻辑的一种简单方法,例如

#include <stdio.h>
#include <stdlib.h>
int countRowsCols (FILE *fp, int *rows, int *cols);
int storeMaze(int N, int M, int (*matrix)[M], FILE *fp);
int main (int argc, char **argv)
{
if (argc != 2) {    /* validate 1 argument given for filename */
fprintf (stderr, "Invalid format.n"
"Correct format: %s maze_file.datn", argv[0]);
exit (EXIT_FAILURE);
}

int M = 0, N = 0;                   /* initialize VLA bounds zero */
FILE *fin = fopen (argv[1], "r");   /* open file */
if (!fin) { /* validate file open for reading ("b" not required) */
fprintf (stderr, "error: file open failed '%s'.n", argv[1]);
exit (EXIT_FAILURE);
}

/* count rows/columns in single pass, update values for N & M */
if (!countRowsCols (fin, &N, &M))   /* VALIDATE return */
return 1;

int maze[N][M];                     /* declare VLA (consider dynamic allocation */
rewind (fin);                       /* rewind file for read into maze */

/* read values into maze */
if (!storeMaze (N, M, maze, fin))   /* VALIDATE return */

fclose (fin);                       /* close file */
for (int i = 0; i < N; i++) {       /* output maze, converting 0,1s to char */
for (int j = 0; j < M; j++)     /* to validate values in maze */
putchar (maze[i][j] ? '#' : ' ');
putchar ('n');
}
}
/* determine number of rows and columns in fp, and
* validate each row has the same number of columns.
* returns 0 on failure, 1 otherwise, the values at
* the addresses for rows and cols are updated to
* hold the number of rows and columns in fp.
*/
int countRowsCols (FILE *fp, int *rows, int *cols)
{
int row = 0,            /* row counter */
col = 0,            /* column counter  */
last = 0;           /* hold char read during last loop iteration */

*rows = *cols = 0;      /* zero value at pointer addresses */

for (;;) {                                  /* loop continually */
int c = fgetc (fp);                     /* read character */
if (c == EOF) {                         /* if EOF */
if (last != 'n') {                 /* if last not n, non-POSIX eof */
row++;                          /* add +1 to row */
if (*cols && col != *cols) {    /* if *cols set and col != *cols */
fputs ("error: unequal number of columns in file.n", stderr);
return 0;
}
}
break;                  /* break on EOF */
}
else if (c == 'n') {       /* if char is 'n' */
if (!*cols)             /* if *cols not set */
*cols = col;        /* set no. of cols based on 1st row */
if (*cols != col) {     /* check col against *cols for remaining lines */
fputs ("error: unequal number of columns in file.n", stderr);
return 0;
}
col = 0;                /* reset column count */
row++;                  /* increment rows */
}
else                        /* normal character */
col++;                  /* increment col count */
last = c;                   /* set last = c */
}

return *rows = row;     /* return result of comparison validating all rows read */
}
/* fill VLA (NxM) from fp setting element 1 if character is '#'
* or 0 for all others. validate n follows each row, optional
* on last line. returns 1 on success, 0 otherwise.
*/
int storeMaze (int N, int M, int (*matrix)[M], FILE *fp)
{
int row = 0;            /* row counter */

while (row < N) {
int c,              /* char to read */
col = 0;        /* column counter */
do {                /* loop M times */
if ((c = fgetc (fp)) == 'n' || c == EOF) {   /* read c, check EOL and EOF */
fputs ("error: premature EOL or EOF.n", stderr);
return 0;
}
matrix[row][col++] = (c == '#') ? 1 : 0;      /* set value, increment col */
} while (col < M);

if ((c = fgetc (fp)) != 'n' && c != EOF) {       /* validate next char is n */
fputs ("error: invalid column count.n", stderr);
return 0;
}
row++;              /* increment row */
}

return row == N;        /* return result of comparison validating all rows read */
}

示例使用/输出

$ /bin/maze_cli dat/maze_cli.txt
################
#              #
###########    #
#              #
## # # #  ######
#              #
#              #
#              #
################

验证迷宫值

您可以通过在迷宫中输出0s 和1s 生成的字符并使用diff将输出与原始数据文件进行比较来验证程序逻辑,例如

$ diff dat/maze_cli.txt <(./bin/maze_cli dat/maze_cli.txt)

或者,确认对输入文件的操作,无论是否使用 POSIX eof,

$ diff <(./bin/maze_cli dat/maze_cli.txt) <(./bin/maze_cli dat/maze_cli_neol.txt)

没有输出确认输入数据文件与从转换回字符的maze值生成的输出之间没有差异。


每次读取使用 fgets() 简化

虽然面向字符的方法很好,但您可以通过使用fgets()countRowsCols()storeMaze()中一次读取一行来简化该过程。您在storeMaze()中包含fgets(),但每行调用它M次,而不是调用一次。

countRowsCols()中,您可以获取每行中的列数,并在一次调用中从由fgets()填充的缓冲区末尾修剪'n'strcspn()。见人 3 strspn。在将行读入缓冲区buf后,可以按如下方式完成:

while (fgets (buf, MAXC, fp)) {             /* read each line into buf */
size_t len;
buf[(len = strcspn (buf, "n"))] = 0;   /* trim newline, save length */

len保存字符数(不包括'n')是该行中的列数。在为每行中的最大字符数声明常量后(例如#define MAXC 1024),包括string.h,您可以执行以下操作:

/* determine number of rows and columns in fp, and
* validate each row has the same number of columns.
* returns 0 on failure, 1 otherwise, the values at
* the addresses for rows and cols are updated to
* hold the number of rows and columns in fp.
*/
int countRowsCols (FILE *fp, int *rows, int *cols)
{
char buf[MAXC];                 /* buffer for each line */
int row = 0;                    /* row counter */

*rows = *cols = 0;              /* zero value at pointer addresses */

while (fgets (buf, MAXC, fp)) {             /* read each line into buf */
size_t len;
buf[(len = strcspn (buf, "n"))] = 0;   /* trim newline, save length */

if (!*cols)                 /* set *cols based on first line no. of columns */
*cols = len;

if (len != (size_t)*cols) { /* check col against *cols for remaining lines */
fputs ("error: unequal number of columns in file.n", stderr);
return 0;
}

row++;                      /* increment row counter */
}

return *rows = row;     /* return result of comparison validating all rows read */
}

您的storeMaze()也同样简化,在阅读后fgets()只需循环访问每行中的M个字符即可填充迷宫,例如

/* fill VLA (NxM) from fp setting element 1 if character is '#'
* or 0 for all others. validate n follows each row, optional
* on last line. returns 1 on success, 0 otherwise.
*/
int storeMaze (int N, int M, int (*matrix)[M], FILE *fp)
{
char buf[MAXC];             /* buffer for each line */
int row = 0;                /* row counter */

while (row < N && fgets (buf, MAXC, fp)) {          /* check bounds, read line */
for (int i = 0; i < M && buf[i]; i++)           /* loop over each char */
matrix[row][i] = (buf[i] == '#') ? 1 : 0;   /* fill maxtrix elements */

row++;                  /* increment row */
}

return row == N;        /* return result of comparison validating all rows read */
}

您可以简单地将函数换成使用fgetc()的函数,除了定义MAXC和包括string.h之外,无需任何其他更改。行计数和列计数的输出和处理是相同的。

仔细查看,如果您有其他问题,请告诉我。

数组的内存不干净。一个简单的技巧是写入一个空字节作为分配(或空格)的一部分。

编辑:对不起!我没有检查您是如何初始化数组的。应使用常量作为上限大小。修复了我的答案

int main(int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "Invalid format.nCorrect format: %s maze_file.datn", argv[0]);
exit(EXIT_FAILURE);
}
FILE *fin = fopen(argv[1], "rb");
if(!fin)
{
fprintf(stderr, "Couldn't open file: %sn", argv[1]);
exit(EXIT_FAILURE);
}
int N = countLines(fin); // lines in the matrix
rewind(fin);
int M = countColumns(fin); // columns in the matrix
rewind(fin);
//  printf("Lines: %d | Columns: %dn", N, M);
int maze[50][50];
storeMaze(N, M, maze, fin);
fclose(fin);
return 0;
}
void storeMaze(int N, int M, int matrix[N][M], FILE *fp)
{
int z=0, i, startPoint, endPoint;
char buf[1000];
while (fgets(buf, sizeof buf, fp)) {
for(i = 0; buf[i] != 'n' && z < N; i++){
if(buf[i] == '#')
{
matrix[z][i] = 0;
}
else if(buf[i] == ' ')
{
matrix[z][i] = 1;
}
printf("%d ", matrix[z][i]);
}
z++;
}
putchar('n');
}

最新更新