Windows 10中控制台窗口的奇怪行为

4

我的控制台窗口使用的是 437 编码页,我已经在控制台窗口中输出了俄文字母:

echo привет

我得到了正确的俄语输出,即:

привет

但是为什么我能得到正确的俄语输出,难道不应该得到6个问号作为输出("??????")?我认为应该得到"??????"作为输出的原因是,在将字符串"echo привет"发送到标准输入缓冲区之前,它应该被转换成437代码页(这将产生"??????",因为那些俄文字母不存在于437代码页中),然后转换后的字符串将被发送到标准输入缓冲区,然后"??????"字符串将被cmd.exe从标准输入缓冲区检索出来,并且cmd.exe将打印它到控制台窗口。
我知道这是会发生的,因为我创建了一个C程序,将其与控制台窗口关联并设置其代码页为437,然后我会向程序发送"привет"俄文字母,然后程序将其打印到控制台窗口中(所打印的是"??????"字符串),这是我的程序代码:
#include <Windows.h>
#include <stdio.h>  

int main()
{
    SetConsoleOutputCP(437);
    SetConsoleCP(437);

    char str[1212];
    gets(str);

    printf(str);

    return 0;
}

我正在使用经典控制台窗口(而非PowerShell),并且正在使用Windows 10。


1
你为什么期望控制台主机要经过所有的麻烦来查询“当前”的代码页,将(UTF-16)输入转换为CP编码,然后执行其操作,并将(假定的)CP编码输出重新编码回UTF-16?我没有查看,但我强烈怀疑解码和重新编码是否正在发生。那么,你需要解决什么问题呢? - IInspectable
@IInspectable我期望控制台窗口这样做,因为它对我的程序也是这样做的,那么为什么它不会对cmd.exe这样做呢?我的意思是,控制台窗口能知道它首先是在和cmd.exe交流吗?也就是说,如果控制台窗口知道它正在与cmd.exe通信,那么它可以决定不去经历你提到的所有麻烦,但我认为控制台窗口不知道它正在和谁交流。 - user8240761
@David Heffernan 我期望的是,我程序中发生的行为也会在cmd.exe中发生,但事实并非如此,我只是想知道原因。 - user8240761
这似乎是一个完全无意义的追求。为什么你想学习一些毫无用处的东西呢?为什么代码页437对你来说很有趣呢? - David Heffernan
如果你想学习“经典”控制台主机的工作原理,可以查看它的源代码。它是公开的 - IInspectable
显示剩余9条评论
1个回答

4

编辑:由于下面与原始帖子的讨论

我只能访问 Windows 10,1903 版本。由于您有更新版本,我认为应该也适用。

CMD 解释器

据我所知,cmd.exe 具有出色的 Unicode 支持,无论您运行哪个代码页都不重要。您的前提是存在代码页转换是错误的。由于 ECHO 命令是内部的 cmd.exe 命令,因此它支持 Unicode。

您在 cmd.exe 中键入 привет 并使用 ECHO 命令意味着您实际上是在使用 Unicode cmd.exe 和 Unicode 命令 ECHO,这将导致即使是页面编码为 437,也会打印一个 Unicode 字符串。 cmd.exe 解释器是控制台的子进程,因为它是从内部工作的,并且具有 Unicode 定义。因此,使用 echo привет 正确地打印了 привет

您的 C 程序

您的 C 语言程序强制控制台代码页在调用进程中转换控制台输入为相应的字符值。这就是为什么您的 ?????? 是因为代码页 437 不理解 Cyrillic 中的 привет

引用SetConsoleCP function MSDN:

设置与调用进程关联的控制台使用的输入代码页。控制台使用其输入代码页将键盘输入转换为相应的字符值。

BONUS

有关 cmd.exe 的更多研究讨论,您可以阅读 SO 上对以下问题的优秀回答(我不想重复信息):

在 Windows 命令提示符 / PowerShell (Windows 10) 中使用 UTF-8 编码(CHCP 65001)

如何在Windows命令行中使用Unicode字符?


“你的前提是cmd.exe是一个控制台是错误的。”我并没有说我认为cmd.exe是一个控制台,当我在我的问题中使用“控制台”一词时,我是在谈论一个与cmd.exe通信的单独程序。 - user8240761
我认为你没有理解我的问题(同时我知道echo是一个内部命令,我忘了在我的第一条评论中提到它)。 我问题最重要的部分是:当您在控制台窗口中键入俄文字母时,这些俄文字母是否会首先转换为控制台输入代码页的字符编码(使用SetConsoleCP()设置),然后将转换后的字符串放置在stdin缓冲区中,然后与控制台窗口相关联的进程将从stdin缓冲区检索此转换后的字符串? - user8240761
@user8240761,显然我对你的问题仍然不是很清楚。你的问题是,如果你在cmd.exe解释器中使用SetConsoleCP()函数将编码设置为437,并运行带有以上源代码的C应用程序(some.exe),为什么你会看到正确的西里尔字母而不是不正确的?????? - tukan
我的问题是,为什么当我在控制台中输入字符串"echo привет"时,cmd.exe显然能够正确接收俄语字母"привет",但是当我在控制台中输入这些相同的俄语字母("привет")发送到我的程序时,我的程序将这个字符串接收为问号!(现在可以理解为字符串“привет”以问号形式被我的程序接收,因为控制台的代码页是437,由于俄语字母不存在于437代码页中,它们将被替换为问号,但为什么cmd.exe没有将“привет”也接收为问号呢?!)。 - user8240761
是的,正是如此,这就是我想表达的意思。 - user8240761
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接