c - 在 C 中读取具有不均匀大小的立方体和额外字符的迷宫文件的数组
问题描述
我试图弄清楚如何正确地从 C 中的 .txt 文档中读取和存储迷宫。这个迷宫的最大尺寸为 40x40“立方体”。读完后,我需要解决它,方法是把从左下角到右下角的路径用 *. 我习惯于使用 2D 数组,但是这个问题一直困扰着我,因为我不明白如果行和列不完全均匀,或者我什至如何准确地在中间打印 *在我解决了每个“立方体”之后。我读过其他由 1 和 0 组成的迷宫示例,或者甚至全部由墙壁组成的 #,这使得阅读和跟踪变得容易,但不能像这样输入。在解决将由双空格分隔的第一个迷宫后,我需要在同一个文本文件中读取其他迷宫。以下是其中一个迷宫的示例:
+---+---+---+---+---+
| | |
+---+ + + + +
| | | | | |
+ + + + +---+
| | | | |
+ + + +---+ +
| | | |
+ +---+---+ + +
| | |
+---+---+---+---+---+
这是我迄今为止所做的一些代码,用于错误检查和读取字符。在其中,我试图初始化一个 120x120 的数组,读取当前字符,并将这些字符转换为 -1 或 0 以对应于墙壁或空白空间。:
/* Read in a grid as parameter of max 120x120 to account for '+' and'-' */
int readLine(int grid2D[120][120])
{
int row = 0;
int col = 0;
int isNewLine = TRUE;
/* Loop through while getchar does not equal EOF */
while ((c = getchar()) != EOF)
{
/* Check for foreign characters, return FALSE if found */
if ((c != '+') || (c != '-') || (c != '|') || (c != ' '))
{
/* If c = \n , avoid tripping error, and do nothing */
if(c == '\n'){}
else
errorFree = FALSE;
}
/* If character is a '+' '-' or '|', it is a wall, set to -1 to
use int's for wall tracking */
else if (row%2 == 0)
{
if(c == '|')
{
grid2D[row][col] = -1;
col++;
}
}
else if((c == '+') || (c == '-'))
{
grid2D[row][col] = -1;
col++;
}
else
{
if(c == '\n')
{
col = 0;
row++;
}
}
isNewLine = TRUE;
return isNewLine;
}
任何指导将不胜感激,我不确定我正在做的方法是否正确。我相信我目前正在正确检查错误,但是我很难理解我应该如何跟踪每个“立方体”,因为每个“立方体”的字符不均匀,它们的尺寸更像 5x1 立方体(a +---+
for一侧和另一侧|
)
解决方案
针对您在评论中提出的问题,确定行和列的大小非常简单。当您从文件中读取一行数组时fgets
,您可以使用strlen()
来确定字符数(但请注意,它还包含该'\n'
字符-因此您需要减去一个-您可以结合修剪'\n'
from结束)
一旦您阅读了第一行并考虑了'\n'
,设置一个变量来保存数组中的字符(列)数。由于您知道您的数组是一个多维数据集,因此您可以将第一行的长度与读取的每隔一行的长度进行比较,以验证所有行的列数相同。
当您循环和处理每一行的输入时,您只需保留一个行计数器,当您完成读取时,它将保存数组中的行数。
有两种方法可以处理阵列的存储。您可以声明一个足够大的数组来容纳您预期的最大迷宫(同时保持它足够小以适合堆栈256x512
在 Linux 和 Windoze 上都是安全的),或者您可以为列和行动态分配存储空间,realloc()
用于分配额外的存储空间按要求。(在那里,您可以处理高达计算机内存限制的迷宫大小 - 但它确实增加了复杂性)
例如,您对我的阵列的“困惑”11x21
是可以理解的。这一切都源于终端上的字符大约是宽度的两倍。因此,要打印一个“立方体”字符,您需要的列数大约是行数的两倍。这根本不是问题。如果您正确地对列和行的读取进行编码,并使用变量来跟踪列和行的数量——那么差异只不过是您的代码在几个变量中跟踪的数字。
以下是一个简短的示例,用于解决您在读取未知数或行和列达到固定最大值时遇到的障碍。(而不是动态分配和重新分配——我们可以留到以后)。为此,我们#define
为最大列数设置一个常数,然后知道我们需要该行数的 1/2,该行数为#define
一个常数,例如
#include <stdio.h>
#include <string.h>
#define MAXC 512 /* declare consts for max NxN you can handle */
#define MAXR (MAXC + 1) / 2
然后,声明变量来跟踪当前的行数和列数 ( ) 以及声明一个数组来保存迷宫是一件row
简单col
的nrow, ncol
事情a[MAXR][MAXC] = {""};
。然后,如果将文件名作为第一个参数给出,则可以打开文件(stdin
如果没有给出参数,则默认读取)。在任何一种情况下,您都可以验证您是否打开了一个流以供阅读,例如
size_t row = 0, col = 0, nrow = 0, ncol = 0;
char a[MAXR][MAXC+1] = {""}; /* delcare and initialize array */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
随着您的文件流打开以供读取,现在只需读取文件中的所有数据行。由于您希望所有行都有偶数个字符,因此您的迷宫实际上是一个立方体,您可以保存第一行中的字符数(在修剪 之后'\n'
)并使用它来比较每个其他行中的字符数行来验证你有一个立方体。当您阅读行时,您还需要保护您的数组边界,这样您就不会尝试在数组中存储比您要保存的行更多的行,因此简单的检查row < MAXR
结合您fgets (a[row], MAXC, fp)
将施加该限制,例如
while (row < MAXR && fgets (a[row], MAXC, fp)) {
size_t len = strlen (a[row]); /* get length of row */
if (len && a[row][len-1] == '\n') /* validate it fits in array */
a[row][--len] = 0; /* remove trailing '\n' char from end */
else if (len == MAXC) {
fprintf (stderr, "error: row exceeds %d chars.\n", MAXC);
return 1;
}
if (!row) /* if 1st row - set expected ncol for each row */
ncol = len;
if (ncol != len) { /* validate all other rows against 1st */
fprintf (stderr, "error: unequal columns (%lu) on row (%lu)\n",
len, row);
return 1;
}
/* your code goes here - example just outputs array */
for (col = 0; col < ncol; col++)
putchar (a[row][col]);
putchar ('\n');
row++; /* advance row counter when done processing row */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
nrow = row; /* save the total number of rows */
您现在拥有存储的所有行和列,并且您已经设置了nrow
和ncol
值,并为您提供了nrow x ncol
数组。我将留给您的路径逻辑,但我确实想提供一个示例,说明如何在您的路径中' '
替换为。'*'
以下对每个可能的路径字符执行此操作,每个可能的路径字符都施加了每个'*'
具有相邻空间的约束(您可以根据需要进行调整)。在这里,我们只是循环0 -> nrow-1
并嵌套一个循环 from 0 -> ncol-1
to 循环遍历数组中的每个字符。
在检查一行中的相邻单元格时唯一需要注意的问题是,在检查左侧的列而不是在迷宫的右边缘时,必须确保自己不在迷宫的左边缘检查右侧的列时(访问数组边界之外的元素将调用Undefined Behavior)
您将边缘检查作为简单地添加到您的if (...)
语句内部的条件,例如
/* you can make multiple passes over the array to determine your path.
* below is just an example of replacing the spaces in the path with
* asterisks.
*/
puts ("\nreplacing path spaces with asterisks\n");
for (row = 0; row < nrow; row++) {
for (col = 0; col < ncol; col++) {
/* if adjacents and current ' ', replace with '*' */
if (col && col < ncol - 1 && /* col > 0 && col < ncol-1 */
/* next checks adjacent and current all ' ' */
a[row][col-1] == ' ' && a[row][col] == ' ' &&
a[row][col+1] == ' ')
a[row][col] = '*'; /* if conditions met, set element '*' */
putchar (a[row][col]);
}
putchar ('\n');
}
将所有部分放在一个简短的示例中以读取任何512
字符宽的迷宫,您可以执行以下操作:
#include <stdio.h>
#include <string.h>
#define MAXC 512 /* declare consts for max NxN you can handle */
#define MAXR (MAXC + 1) / 2
int main (int argc, char **argv) {
size_t row = 0, col = 0, nrow = 0, ncol = 0;
char a[MAXR][MAXC+1] = {""}; /* delcare and initialize array */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (row < MAXR && fgets (a[row], MAXC, fp)) {
size_t len = strlen (a[row]); /* get length of row */
if (len && a[row][len-1] == '\n') /* validate it fits in array */
a[row][--len] = 0; /* remove trailing '\n' char from end */
else if (len == MAXC) {
fprintf (stderr, "error: row exceeds %d chars.\n", MAXC);
return 1;
}
if (!row) /* if 1st row - set expected ncol for each row */
ncol = len;
if (ncol != len) { /* validate all other rows against 1st */
fprintf (stderr, "error: unequal columns (%lu) on row (%lu)\n",
len, row);
return 1;
}
/* your code goes here - example just outputs array */
for (col = 0; col < ncol; col++)
putchar (a[row][col]);
putchar ('\n');
row++; /* advance row counter when done processing row */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
nrow = row; /* save the total number of rows */
/* you can make multiple passes over the array to determine your path.
* below is just an example of replacing the spaces in the path with
* asterisks.
*/
puts ("\nreplacing path spaces with asterisks\n");
for (row = 0; row < nrow; row++) {
for (col = 0; col < ncol; col++) {
/* if adjacents and current ' ', replace with '*' */
if (col && col < ncol - 1 && /* col > 0 && col < ncol-1 */
/* next checks adjacent and current all ' ' */
a[row][col-1] == ' ' && a[row][col] == ' ' &&
a[row][col+1] == ' ')
a[row][col] = '*'; /* if conditions met, set element '*' */
putchar (a[row][col]);
}
putchar ('\n');
}
return 0;
}
示例使用/输出
正如所指出的,代码只是读取并输出原始迷宫,然后在迷宫上进行第二次传递,输出它的路径填充有'*'
$ ./bin/array2dread <dat/arrmaze.txt
+---+---+---+---+---+
| | |
+---+ + + + +
| | | | | |
+ + + + +---+
| | | | |
+ + + +---+ +
| | | |
+ +---+---+ + +
| | |
+---+---+---+---+---+
replacing path spaces with asterisks
+---+---+---+---+---+
| * * * | * * * * * |
+---+ * + * + * + * +
| * | * | * | * | * |
+ * + * + * + * +---+
| * | * | * | * * * |
+ * + * + * +---+ * +
| * * * | * | * * * |
+ * +---+---+ * + * +
| * * * * * * * | * |
+---+---+---+---+---+
如果您还有其他问题,请仔细查看并告诉我。
推荐阅读
- python - 在 keras 中可视化训练好的神经网络的权重
- java - 根据 Yaml shema 验证 JSON 数据
- django-models - 如何在 Django 中为购物车使用启动功能?
- java - CodenameOne 字体图像无法识别
- sql - “聚合可能不会出现在 UPDATE 语句的集合列表中”。同时执行 2 个 SQL 查询
- r - read_html 在 rstudio 服务器上不起作用
- javascript - Fabric.js 一直无法操作新加载的背景图片
- c++ - c ++是否有任何Windows系统程序可以加载我的代码以保护我的线程不被杀死(CreateRemoteThread)
- mysql - SQL 根据另一个表和另一个列值删除一个表中的所有行
- python - python中xaxis(axhspan,matplotlib)上带有日期时间的绘图中的阴影区域