如何使用 Windows.h 读取和显示扩展的 ASCII 符号

How to read and display extended ASCII symbols with Windows.h

我正在开发一款使用 ASCII 符号作为像素的主机游戏。此游戏的地图存储在 .txt 文件中:

████████████████
█              █
█              █
█              █
█              █
█              █
█              █
█              █
█              █
█              █
█              █
█              █
█              █
█              █
█              █
████████████████

为了显示地图,我正在逐行从文件 demo.txt 中读取它并将每个字符写入 CHAR_INFO *screen:

void setScreen(const char* layoutFile, const char* levelDataFile) {
        std::ifstream levelData(levelDataFile);
        levelData >> width >> height;
        field = {0, 0, (SHORT)width, (SHORT)height};
        screen = new CHAR_INFO[width * height];
        levelData.close();

        std::ifstream layout(layoutFile);                            //reading from a file `demo.txt`
        std::string line;

        for (int j = 0; j < height; j++) {
            getline(layout, line);
            for(int i = 0; i < width; i++) {
                screen[j * width + i].Char.AsciiChar = line[i];      //writing each character of a line to screen
                screen[j * width + i].Attributes = BACKGROUND_GREEN;
            }
        }
        layout.close();
    }

之后,我使用以下函数显示地图(map.getScreen() returns 指向屏幕数组的指针):

WriteConsoleOutputA(
            console.getHOut(),
            map.getScreen(),
            { (SHORT)map.getWidth(), (SHORT)map.getHeight() },
            { 0,0 },
            &map.getField()
        );

但问题是显示为,输出如下:

����������������
���
���
���
���
���
���
���
���
���
���
���
���
���
���
����������������

我试过的一些东西:

来自评论:

File is encoded in UTF-8.

这有很大的不同。您不仅不处理 ASCII 字符(值最多 127),而且甚至不处理扩展的 ASCII 字符(值最多 255)。您正在处理 Unicode,特别是字符编号 9608 (a.k.a.U+2588)。这远远超出了单个 char 所能代表的范围。然而,当您从 line[i].

分配时,您正在存储单个 char

'█' 的 UTF-8 表示由三个字节组成:0xE20x960x88。这就是为什么您的输出在板的左侧显示三个“未知字符”符号,在右侧显示 none。那些“未知字符”符号来自一个 UTF-8 字符的三个字节。然后你会有 width-2 空格后跟三个更多的“未知字符”,除非你在 width-3 空格后停止复制字符。所以你永远不会遇到你的董事会的“真实”右边界。 (检查 line 的长度并将其与 width 进行比较——对于中间行,您应该看到 line.size()width+4。对于第一行和最后一行,您应该看到 line.size()3*width。)

部分解决方案是使用 Char.UnicodeChar 而不是 Char.AsciiChar。但是,UnicodeChar(我认为)只有两个字节,所以它不能容纳三字节的 UTF-8 编码。您可能必须转换为 UTF-16。如果您只需要几个字符,查找 table 可能与通用解决方案一样有效。通过口述等效项将文件中的字符更改为真正的 ASCII 字符。例如,也许你可以说 '#' 代表一个完整的块。这具有作为单字节的优势,因此您的逻辑大部分都有效。您只需要添加一个翻译功能,例如

WCHAR convert(char c)
{
    switch ( c ) {
        case '#': return u'[=10=]x2588';  // Full block (█)
        // Etc.
    }
    return c; // If no translation is needed
}

然后在存储你的地图数据时,你会调用这个翻译,如

screen[j * width + i].Char.UnicodeChar = convert(line[i]);

最后一步是确保您的控制台支持 UTF-16。哦,使用 WriteConsoleOutputW() 而不是 WriteConsoleOutputA()