繁体   English   中英

将迷宫存储到二维数组中

[英]Storing maze into 2d array

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

################
#              #
###########    #
#              #
## # # #  ######
#              #
#              #
#              #
################
#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.dat\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    FILE *fin = fopen(argv[1], "rb");

    if(!fin)
    {
        fprintf(stderr, "Couldn't open file: %s\n", 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: %d\n", 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');
}

根据文件中的每个字符,output中应该只有一个和零,其中一些打印正确,但大多数是垃圾值。 直到现在我还没有真正使用过矩阵,所以我猜也许我在 storeMaze 中传递了错误的参数或其他东西。 有任何想法吗?

阵列的 memory 不干净。 一个简单的技巧是写一个空字节作为分配(或空间)的一部分。

编辑:对不起。 我没有查看您是如何初始化 arrays 的。 您应该使用常量作为上限大小。 修正了我的答案

int main(int argc, char **argv)
{
    if(argc != 2)
    {
        fprintf(stderr, "Invalid format.\nCorrect format: %s maze_file.dat\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    FILE *fin = fopen(argv[1], "rb");

    if(!fin)
    {
        fprintf(stderr, "Couldn't open file: %s\n", 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: %d\n", 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');
}

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

您的countLines()countColumns()应该组合起来,因为列数和行数都可以在一次通过文件中确定。 只需将NM的地址作为参数传递,一旦在 function 中确定了行数和列数,分别更新NM持有的地址处的值。

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

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

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

确定行和列的一种简单方法是使用fgetc()循环连续读取,并在读取文件时跟踪行数和列数。 跟踪last迭代中的字符将使您确定文件是否在文件中的最后一行数据(POSIX 文件结尾)之后为'\n' ,如果不是,则将1添加到您的行数.

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

/* 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'并重置下一行的列数,并像 go 一样验证行数。 当您到达末尾时,请确认您已阅读预期的列字符行数。 你可以这样写:

/* 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() function 的成功或失败返回给调用者。对于任何对代码的持续操作至关重要的 function,您必须验证function 是成功还是失败。为您的 function 选择一个void类型会丢弃您将成功或失败传达回调用 ZC1C425268E68385D1AB5074C17A 的main()此处主要)。

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

#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.dat\n", 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
################
#              #
###########    #
#              #
## # # #  ######
#              #
#              #
#              #
################

验证迷宫值

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

$ 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)

没有 output 确认输入数据文件和从maze值转换回字符生成的 output 之间没有区别。


为每次读取简化使用 fgets()

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

countRowsCols()中,您可以在一次调用strcspn() () 时获取每行中的列数并从fgets()填充的缓冲区末尾修剪'\n' man 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 */
}

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM