【问题标题】:How to read and display extended ASCII symbols with Windows.h如何使用 Windows.h 读取和显示扩展的 ASCII 符号
【发布时间】:2021-03-14 00:22:16
【问题描述】:

我正在开发一个控制台游戏,它使用 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() 返回指向屏幕数组的指针):

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

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

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

我尝试过的一些事情:

  • SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8);
  • SetConsoleCutputCP(1251);
  • setlocale(LC_ALL, "");
  • std::locale::global(std::locale(std::locale::empty(), new std::codecvt_utf8&lt;wchar_t&gt;));

【问题讨论】:

  • 文件是如何编码的?您是否尝试过使用std::wfstream 而不是std::fstream
  • @alterigel 是的,我尝试使用std::wifstream,结果是一样的。文件编码在UTF-8
  • @JaMiT,我也试过SetConsoleCutputCP(437),但没有任何改变

标签: c++ character-encoding console


【解决方案1】:

来自评论:

文件编码为UTF-8

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

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

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

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

然后在存储地图数据时,您将调用此翻译,如

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 2013-06-26
    • 1970-01-01
    • 2015-04-26
    相关资源
    最近更新 更多