我正在尝试将以下迷宫从作为命令行参数给出的文件编码为 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()
,因为可以在一次传递文件中确定列数和行数。只需将N
和M
的地址作为参数传递,一旦在函数中确定了行数和列数,就分别更新N
和M
持有的地址处的值。
确定行数和列数时,必须验证文件中的每一行是否具有相同的列数。如果没有,则该文件没有适合您的迷宫的有效格式。
在确定文件中的行数时,必须处理文件包含非 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
################
# #
########### #
# #
## # # # ######
# #
# #
# #
################
验证迷宫值
您可以通过在迷宫中输出0
s 和1
s 生成的字符并使用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');
}