如何在 wchar_t 数组中添加换行符?
How can I add line breaks in a wchar_t array?
我正在开发主机游戏。它使用屏幕缓冲区在更新地图后刷新控制台 window。
这是主要的 while
循环。
while (true) {
//player.doStuff(_kbhit());
//map.update();
WriteConsoleOutputCharacter(
console.getScreenBuffer(),
(LPWSTR)map.getScreen(),
map.getWidth() * map.getHeight(),
{ 0, 0 }, console.getBytesWritten()
);
Sleep(1000 / 30);
}
在此循环之前,我从 .txt
文件中获取地图布局:
class Map {
int width, height;
wchar_t* screen;
public:
wchar_t* getScreen() {
return screen;
}
void setScreen(std::string layoutFile, std::string levelDataFile) {
std::ifstream levelData(levelDataFile);
levelData >> width >> height;
screen = new wchar_t[(width + 1) * height];
levelData.close();
std::wifstream layout(layoutFile);
std::wstring line;
for (int j = 0; j < height; j++) {
std::getline<wchar_t>(layout, line);
for(int i = 0; i < width; i++) {
screen[j * width + i] = line.at(i);
}
screen[width * (j + 1)] = L'\n';
}
layout.close();
}
};
map.setScreen("demo.txt", "demo_data.txt");
问题是打印的地图显示为一个字符串,没有任何换行符,如下所示:
00000__00000
当我期望它看起来像这样时:
0000
0__0
0000
我试过在每行写完后添加L'\n'
、L'\r\n'
,但是没有用。
简而言之
这里有两个独立的问题:
- 第一个是换行符被覆盖。
- 第二个,一旦你纠正了第一个,windows 控制台 API 不处理换行符
更多详情
覆盖问题
我假设宽度不包括每行末尾的换行符,因为您的屏幕分配是:
new wchar_t[(width + 1) * height]; // real width is width + 1 for '\n'
现在看看您的逻辑,您添加到该行的最后一个 '\n'
设置为:
screen[ width * (j + 1) ] // same as screen[ j * width + width ]
根据您的索引方案,这似乎很好,因为您将布局字符复制到:
screen[ j * width + i ]` // where `i` is between `0` and `width-1`
所以换行符会在 screen[ j * width + width ]
.
不幸的是,使用您的索引公式,下一行的第一个字符会覆盖同一位置:将 j
替换为 j+1
并将 i
替换为 0
给出:
screen[ (j + 1) * width + 0 ]
也就是
screen[ (j + 1) * width ] // same location as screen [ width * (j+1)]
每行都有尾随换行的解决方法
更正您的索引方案,考虑到线的实际宽度是 width+1
。
所以索引公式变为:
for(int i = 0; i < width; i++) {
screen[j * (width+1) + i] = line.at(i);
}
当然还有结尾的换行符:
screen[j * (width+1) + width] = L'\n'; // self-explaining
screen[(j+1) * (width+1) -1] = L'\n'; // or alternative
控制台的问题API
WriteConsoleOutputCharacter()
不提供对换行符和控制字符的真正支持。这些显示为问号。
文档提到了根据控制台模式处理这些控制字符的可能性,但我已经尝试使用 Windows 10,甚至使用变体 WriteConsoleOutputCharacterA()
(可以肯定排除宽字符的问题),它根本不起作用。
您有两个解决方案来完成这项工作,但这两个需要一些修改:
- 逐行显示输出并相应地控制光标位置
- 使用
WriteConsoleOutput()
允许您指定目标矩形(高度和宽度)并在矩形中写入字符而无需换行。不幸的是,数组应该是 CHAR_INFO 而不是简单的字符。
第二种方式的例子:
std::string w = "SATORAREPOTENETOPERAROTAS";
SMALL_RECT sr{ 2, 2, 6, 6 };
CHAR_INFO t[25];
for (int i = 0; i < 25; i++) { t[i].Char.AsciiChar = w[i]; t[i].Attributes = BACKGROUND_GREEN; }
WriteConsoleOutputA(
hOut,
t,
{ 5,5 },
{ 0,0 },
&sr
);
我正在开发主机游戏。它使用屏幕缓冲区在更新地图后刷新控制台 window。
这是主要的 while
循环。
while (true) {
//player.doStuff(_kbhit());
//map.update();
WriteConsoleOutputCharacter(
console.getScreenBuffer(),
(LPWSTR)map.getScreen(),
map.getWidth() * map.getHeight(),
{ 0, 0 }, console.getBytesWritten()
);
Sleep(1000 / 30);
}
在此循环之前,我从 .txt
文件中获取地图布局:
class Map {
int width, height;
wchar_t* screen;
public:
wchar_t* getScreen() {
return screen;
}
void setScreen(std::string layoutFile, std::string levelDataFile) {
std::ifstream levelData(levelDataFile);
levelData >> width >> height;
screen = new wchar_t[(width + 1) * height];
levelData.close();
std::wifstream layout(layoutFile);
std::wstring line;
for (int j = 0; j < height; j++) {
std::getline<wchar_t>(layout, line);
for(int i = 0; i < width; i++) {
screen[j * width + i] = line.at(i);
}
screen[width * (j + 1)] = L'\n';
}
layout.close();
}
};
map.setScreen("demo.txt", "demo_data.txt");
问题是打印的地图显示为一个字符串,没有任何换行符,如下所示:
00000__00000
当我期望它看起来像这样时:
0000
0__0
0000
我试过在每行写完后添加L'\n'
、L'\r\n'
,但是没有用。
简而言之
这里有两个独立的问题:
- 第一个是换行符被覆盖。
- 第二个,一旦你纠正了第一个,windows 控制台 API 不处理换行符
更多详情
覆盖问题
我假设宽度不包括每行末尾的换行符,因为您的屏幕分配是:
new wchar_t[(width + 1) * height]; // real width is width + 1 for '\n'
现在看看您的逻辑,您添加到该行的最后一个 '\n'
设置为:
screen[ width * (j + 1) ] // same as screen[ j * width + width ]
根据您的索引方案,这似乎很好,因为您将布局字符复制到:
screen[ j * width + i ]` // where `i` is between `0` and `width-1`
所以换行符会在 screen[ j * width + width ]
.
不幸的是,使用您的索引公式,下一行的第一个字符会覆盖同一位置:将 j
替换为 j+1
并将 i
替换为 0
给出:
screen[ (j + 1) * width + 0 ]
也就是
screen[ (j + 1) * width ] // same location as screen [ width * (j+1)]
每行都有尾随换行的解决方法
更正您的索引方案,考虑到线的实际宽度是 width+1
。
所以索引公式变为:
for(int i = 0; i < width; i++) {
screen[j * (width+1) + i] = line.at(i);
}
当然还有结尾的换行符:
screen[j * (width+1) + width] = L'\n'; // self-explaining
screen[(j+1) * (width+1) -1] = L'\n'; // or alternative
控制台的问题API
WriteConsoleOutputCharacter()
不提供对换行符和控制字符的真正支持。这些显示为问号。
文档提到了根据控制台模式处理这些控制字符的可能性,但我已经尝试使用 Windows 10,甚至使用变体 WriteConsoleOutputCharacterA()
(可以肯定排除宽字符的问题),它根本不起作用。
您有两个解决方案来完成这项工作,但这两个需要一些修改:
- 逐行显示输出并相应地控制光标位置
- 使用
WriteConsoleOutput()
允许您指定目标矩形(高度和宽度)并在矩形中写入字符而无需换行。不幸的是,数组应该是 CHAR_INFO 而不是简单的字符。
第二种方式的例子:
std::string w = "SATORAREPOTENETOPERAROTAS";
SMALL_RECT sr{ 2, 2, 6, 6 };
CHAR_INFO t[25];
for (int i = 0; i < 25; i++) { t[i].Char.AsciiChar = w[i]; t[i].Attributes = BACKGROUND_GREEN; }
WriteConsoleOutputA(
hOut,
t,
{ 5,5 },
{ 0,0 },
&sr
);