我正在尝试用 C 语言制作一款 2 人国际象棋游戏,该游戏可以在终端中轻松运行。我在字体方面遇到了很多问题,所以我找到了一种好看的字体(最重要的是等宽字体),然后使用 FontForge 将国际象棋符号添加到 LucidaConsole 字体中。
我将符号从 192 改为 217,因为我确信在游戏中我不需要这些符号:
我创建了一个字符串数组:
char board[64][4] = {
"È","Ö","É","Ó","Æ","Õ","Ê","Ô",
"×","Ë","×","Ë","×","Ë","×","Ë",
"Ø","Ù","Ø","Ù","Ø","Ù","Ø","Ù",
"Ù","Ø","Ù","Ø","Ù","Ø","Ù","Ø",
"Ø","Ù","Ø","Ù","Ø","Ù","Ø","Ù",
"Ù","Ø","Ù","Ø","Ù","Ø","Ù","Ø",
"Å","Ñ","Å","Ñ","Å","Ñ","Å","Ñ",
"Î","Ä","Ï","Á","Ì","Ã","Ð","Â"
};
for(int i = 0; i < 64; i++) {
printf("%s", board[i]);
if( (i+1) % 8 == 0 ) {
printf("\n");
}
}
然后在终端中,我期望它看起来像一个普通的棋盘,但我得到的却是这样的:
Alt+0192另外,我实际上可以使用从到的代码手写它们Alt+0217:
我会以某种方式完成编码部分,但如何让这些符号在终端上正确显示?
21
最佳答案
2
文本编码不匹配
您的终端设置为使用文本编码(每个字符一个字节),但您的应用程序输出的字节代表编码文本,而对于您使用的字符,每个字符都被编码为两个字节。
文本编码是“带外”信息:通信双方必须事先就此达成一致。如果他们不同意(正如这里发生的那样),您将得到无意义的输出。这就是为什么您应该尽可能避免处理文本编码,而做到这一点的方法是使用 Unicode。
使用 Unicode
好消息:您的终端支持 Unicode,那么为什么不让它为您处理渲染呢?Unicode 已在代码点 U+2654 至 U+265F 处定义了国际象棋符号。您需要将棋子代码点编码为 UTF-8,或者您也可以从中剪切并粘贴到您的代码中:
"♔","♕","♖","♗","♘","♙", // WHITE k q r b k p
"♚","♛","♜","♝","♞","♟︎" // BLACK k q r b k p
您还需要配置您的终端以使用 UTF-8 编码(查找名为“文本编码”的设置)。
顺便说一句,破解字体是个糟糕的主意。我的意思是除了你(在你的系统上)没有人可以使用该程序,而且你也不能使用该终端正确显示文本。
…并使用终端颜色代码
的答案很好地概括了它们的工作原理。
|
确实,这是一个编码问题,解决方案是使用 Unicode。但我们没有足够的信息来准确指出编码不匹配(或不匹配)的具体位置。
- 源文件编码可能不是编译器所认为的。
- 编译器所针对的执行字符集可能不合适。
- 执行 printf 时,C 运行时库的区域设置可能会产生干扰。默认区域设置是“C”,但我们不知道这是否是当前设置。
- C 运行时实现可能试图转换为宽字符,以调用 Windows 控制台 API 的“宽”版本来输出文本,或者可能调用其中一个“ANSI”API 让 Windows 进行转换。如果它调用的是 ANSI API,那么它是指定正确的代码页还是让 Windows 推断它?
- 终端的代码页可能正确,也可能不正确。
- 字体中的字形重新映射是否正确完成?
- 字体链接或字体回退是否会干扰程序运行时的字体选择?
您想从端到端控制编码。
-
确保源文件是 UTF-8 编码的。在 Posix 领域,这可能是既定事实。但在 Windows 上并非如此。在您的 IDE 或编辑器中,应该有一种方法可以在保存文件时选择编码。您可能可以选择使用或不使用“签名”来保存它。(签名是以 UTF-8 编码的 Unicode 字节顺序标记。)即使是 Unicode 联盟也不鼓励使用签名,但在 Windows 上它会很有用。请自行选择。
-
告诉编译器您的源代码是 UTF-8 编码的。您还想告诉它使用 UTF-8 作为执行字符集。在当前版本的 MSVC 中,有一个 /UTF-8 命令行选项。我相信所有其他编译器都有类似的设置。
-
确保 C 运行时库的语言环境是 C 语言环境。这是默认设置。确保不要更改它,并且不要使用可能更改它的其他库。
-
现在,可以像在 Posix 环境中一样为 Windows 编写文本模式应用程序:通过标准 IO 使用 UTF-8(和终端转义序列)。但是,由于许多旧程序不以这种方式工作,因此您必须先选择加入:
-
调用
SetConsoleOutputCP(CP_UTF8)
以告诉终端您将以 UTF-8 发送文本。(程序结束时我会恢复原始代码页。) -
您可能还想将输入代码页设置为 CP_UTF8。
-
如果您想使用终端转义序列(允许您控制颜色),您必须像这样选择:
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD mode = 0; GetConsoleMode(hOut, &mode); SetConsoleMode(hOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
-
|
[64][4]
?我觉得这可能有关。您是否只是增加了4
直到它在编译时停止抱怨?因为,如果这些字符(正如您似乎认为的那样)都是 192 到 217 之间的所有字符(并且 192 到 217 之间有 26 个字符,而不是 64 个。这是另一个我们无法在没有任何代码的情况下理解的谜团),那么您只需要 2 个字节(字符本身和终端 0,因为它们是字符串)。但是,如果您在不知情的情况下实际上使用 utf-8 作为输入(毕竟,几十年来这一直是所有操作系统的标准),–
[3]
应该足够了。但也许我错过了一个 3 个字节长的字符)–
–
–
–
|