c - C - 未知维度的2D游戏板的动态分配
问题描述
我想编写一个基于棋盘的游戏,并希望将棋盘表示为 2Dchar
数组。特别是,该板由 4 种不同类型的字符/单元格组成:
- 播放器 (
S
) - 目标单元格 (
A
) - 墙壁 (
#
) - 怪物 (
^
,v
,<
,>
)
怪物朝某个方向看,上面的箭头状字符表示。
我希望能够从以下未知的文本文件中加载关卡:
- 文件中的行数(二维数组中的行)
- 每行中的字符数
一个示例级别文本文件:
###### ##########
# < # # #
# ######### # ##
# # # #^ #
# # # ## #
### # ######## ### #
# # ###### # ########## # ##
# #^ # # #########
如您所见,前 3 行有 49 个字符,但其他行包含 48/47 个字符。差异可能要大得多,因此我需要对两个维度都使用动态分配。
我应该使用固定大小的缓冲区逐个字符还是逐行读取文件,并在必要时扩展它?
这是我尝试过的:
int needed_num_rows = 1;
int needed_bytes_col = 16;
char **board = calloc(needed_num_rows, sizeof(char*));
char *buf = calloc(needed_bytes_col, sizeof(char));
int idx = 0;
while (fgets(buf, needed_bytes_col, level_file) != NULL) {
if (buf[needed_bytes_col - 1] != '\n' && buf[needed_bytes_col - 1] != '\0') { // not read to the end yet
needed_bytes_col = needed_bytes_col * 2 + 1;
buf = realloc(buf, needed_bytes_col);
buf += needed_bytes_col;
} else { // whole line has been read
if (idx + 1 > needed_num_rows) {
needed_num_rows = needed_num_rows * 2 + 1;
board = realloc(board, needed_num_rows);
}
board[idx++] = buf;
needed_bytes_col = 16;
buf = calloc(needed_bytes_col, sizeof(char));
}
}
解决方案
这似乎有点复杂,但是你看它。我使用mmap()
了和一个行解析功能。这个函数只做换行查找,加上跳过重复的并检查长度。这个想法是有三个返回:-1 = 完成,-2 = 跳过这一行,正 = 使用这一行(或使用第二个参数,例如结构)。
因此,即使只解析行的存在和长度,它仍然是某种解析。主要部分是:
plret = parse_lines(&pmap)
pmap
是mmap()
映射的移动副本。parse_lines()
推进它。plret
告诉您是 1) 停止 2) 继续还是 3) 行动并继续。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
/* file must have NL at end, and memory a \0 */
int parse_lines(char **pmap) {
int const maxline = 60;
char *map = *pmap;
while(*map == '\n') //skip empty lines???
map++;
if (! *map)
return -1; // means EOF
char *nnl = strchr(map, '\n');
*pmap = nnl + 1; // new position for caller: start of next line
if (nnl - map < maxline)
return nnl - map; // return length of line
else
return -2; // too long
}
void parse_file() {
char const *f = "test";
size_t const mmlen = 256*256; // or fstat()
int fd = open(f, O_RDONLY);
char *mapping = mmap(0, mmlen, PROT_READ, MAP_PRIVATE, fd, 0),
*pmap = mapping;
int plret;
while ((plret = parse_lines(&pmap)) != -1)
if (plret > 0) {
char *s = calloc(plret+1, 1);
memcpy(s, pmap-plret-1, plret); // NL (+1) or not?
printf("%d at %p now %p:\t\"%s\"\n", plret, pmap, s, s);
free(s); // rather store it in an array
}
else
printf("line too long - skip or truncate or abort\n");
/* End of File */
munmap(mapping, mmlen);
// + realloc array for @s-pointers, if it was allocated "big enough"
}
int main(void) {
parse_file();
}
我遗漏了数组arr[idx++] = s
(没有检查/重新分配)。但是输出很好地说明了正在发生的事情(倒数第三行超长):
53 at 0x7fdc29283036 now 0x5557230822a0: " ###### ##########"
53 at 0x7fdc2928306c now 0x5557230826f0: " # < # # #"
53 at 0x7fdc292830a2 now 0x555723082730: " # ######### # ##"
52 at 0x7fdc292830d7 now 0x555723082770: " # # # #^ #"
52 at 0x7fdc2928310c now 0x5557230827b0: " # # # ## #"
52 at 0x7fdc29283141 now 0x5557230827f0: " ### # ######## ### #"
52 at 0x7fdc29283176 now 0x555723082830: " # # ###### # ########## # ##"
51 at 0x7fdc292831aa now 0x555723082870: " # #^ # # #########"
line too long - skip or truncate or abort
2 at 0x7fdc292831fd now 0x5557230828b0: "##"
1 at 0x7fdc292831ff now 0x5557230828d0: "#"
0x7fd 处的字节......在 0x555 处作为字符串找到了一个新家......
他们丢失了换行符,但现在有一个空终止符。
推荐阅读
- c# - 使用 OpenCV 连续捕获桌面循环
- nuget - TeamCity 在应该发布 4 时仅将 1 个包发布到 NuGet 提要
- c# - 如何让安装程序注册一个 IE 插件
- teamcity - 停止 TeamCity 从更新命令行的程序中捕获许多日志行
- c# - 是否有使用国家/地区名称从 UTC 时间转换为本地时间的功能?
- excel-formula - Excel #REF!将工作公式应用于外部文件时出错
- c++ - 使用 Qt 5.11 和 Crypto++ 在 QtCreator 中检测到“RuntimeLibrary”不匹配
- java - 我无法使用 for 循环访问数组的索引,但我可以手动完成
- angular-material - mat-form-field 必须包含一个 MatFormFieldControl。即使我添加了 MatFormFieldModule
- org-mode - 是否可以只折叠一个子树而不必循环 TAB 功能?