如何在Windows命令行中使用Unicode字符?
如何在Windows命令行中使用Unicode字符?
我们在Team Foundation Server(TFS)中有一个项目,其中包含一个非英语字符(š)。在尝试脚本化一些与构建相关的事情时,我们遇到了一个问题;我们无法将š字母传递给命令行工具。命令提示符或其他任何东西都会弄乱它,tf.exe实用程序无法找到指定的项目。
我尝试了不同格式的.bat文件(ANSI,带或不带BOM的UTF-8)以及使用JavaScript进行脚本编写(它本身就是Unicode),但都没有成功。我该如何执行一个程序并传递给它一个Unicode命令行?
如何在Windows命令行中使用Unicode字符?
背景:多年来,我一直在控制台中使用Unicode的输入/输出(并且每天都会经常使用)。而且,我为这个任务开发了支持工具。只要你理解以下事实/限制,几乎没有问题:
- CMD和“console”是无关的因素。CMD.exe只是一个准备在控制台(“控制台应用程序”)中“工作”的程序之一。
- 据我所知,CMD对Unicode有完美的支持;在任何代码页处于活动状态时,您可以输入/输出所有Unicode字符。
- Windows的控制台对Unicode有很多支持,但并非完美(只是“足够好”;请参见下文)。
- chcp 65001非常危险。除非程序专门设计来解决Windows API中的缺陷(或使用具有这些解决方案的C运行时库),否则它将无法可靠地工作。Windows 8修复了关于cp65001问题的一半,但其余问题仍适用于Windows 10。
- 我在Windows-1252中工作。正如我之前所说:要在控制台中输入/输出Unicode,不需要设置代码页。
细节:
要在控制台中读取/写入Unicode,应用程序(或其C运行时库)应该足够聪明,使用的是Console-I/O API而不是File-I/O API。(例如,请参见Python的实现)。
同样,要读取Unicode命令行参数,应用程序(或其C运行时库)应该足够聪明,使用相应的API。
控制台字体呈现仅支持BMP中的Unicode字符(换句话说:小于U+10000)。仅支持简单的文本呈现(因此欧洲和一些东亚语言应该可以正常工作,只要使用预组合形式)。这里对东亚和字符U+0000、U+0001、U+30FB有一些细微的限制。
实际考虑:
Windows的默认设置并不是很有帮助。为了获得最佳体验,应该调整三个配置:
- 输出:全面的控制台字体。为了获得最佳结果,我推荐使用我的构建。(安装说明在那里提供,并且也在本页的其他答案中列出)
- 输入:强大的键盘布局。为了获得最佳结果,我推荐使用我的布局。
- 输入:允许十六进制输入Unicode。
在控制台应用程序中,将字符“粘贴”到控制台应用程序中还存在一个问题:
- 十六进制输入在Alt键的KeyUp事件上提供字符;其他所有提供字符的方式都在KeyDown事件上发生;因此,许多应用程序无法准备好在KeyUp事件上看到字符。(仅适用于使用Console-I/O API的应用程序。)
- 结论:许多应用程序在十六进制输入事件上可能会跳过字符。
- 此外,“粘贴”字符的结果取决于当前的键盘布局:如果可以在不使用前缀键的情况下输入字符(但可以使用任意复杂的修饰符组合,如Ctrl + Alt + AltGr + Kana + Shift + Gray),则会模拟键按下。这是任何应用程序期望的结果,因此粘贴只包含这些字符的任何内容都是可以的。
- 但是,“其他”字符通过模拟十六进制输入传递。
结论:除非您的键盘布局支持在没有前缀键的情况下输入大量字符,否则一些有缺陷的应用程序可能会在通过控制台的UI进行粘贴时跳过字符。这就是为什么我建议使用我的键盘布局!
此外,Windows的“替代,更强大的控制台”实际上根本不是控制台。它们不支持Console-I/O API,因此依赖这些API工作的程序将无法正常运行。一个例子是微软的PowerShell。我不使用它;要尝试,按下并释放Windows键,然后键入powershell。
- 设置字体、键盘布局(可选,允许十六进制输入)。
- 仅使用通过Console-I/O API的程序,并接受Unicode命令行参数。例如,任何使用Cygwin编译的程序都应该可以正常工作。正如我之前所说,CMD也可以。
更新:最初,由于cp65001中的一个错误,我混淆了内核和CRTL层(以及Windows用户模式API!)。此外,Windows 8解决了此错误的一半;我澄清了关于“更好的控制台”应用程序的部分,并添加了有关Python如何处理此问题的参考。
如何在Windows命令行中使用Unicode字符?
在Windows命令行中使用Unicode字符的方法是通过改变代码页为UTF-8(chcp 65001),并且需要使用Lucida控制台字体。然而,需要注意的是Windows的代码页65001支持中存在严重的实现错误,这将导致许多依赖于C标准库IO方法的应用程序出现问题。不幸的是,UTF-8在Windows中是一个二等公民。
关于Windows代码页65001支持中的错误的一个例子是,返回字节数的调用实际上返回的是字符数。这会导致各种各样的问题,例如不完整的输入读取、fflush中的挂起、损坏的批处理文件等等。CJK“多字节”区域设置使用的默认代码页具有特殊处理来解决这个问题,但代码页65001没有 - 它是不受支持的。
要将UTF-8设置为默认编码,可以进入[HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\Autorun]并设置为chcp 65001。
控制台(conhost.exe)对代码页65001的支持在Windows 7中基本上是错误的(对输入和输出都是如此),但在Windows 10中对输入仍然是错误的。请删除这个建议,以避免在一个天真的“帮助”的无尽循环中重复这个错误的建议。cmd shell是一个使用控制台的UTF-16 API和基本API CreateProcessW
和ShellExecuteExW
的Unicode应用程序。如果处理命令行有问题,那是因为应用程序使用了标准C main
的ANSI编码char *
版本,而不是wmain
入口点的wchar_t *
。
UTF-8在控制台中只能部分地且仅用于输出。此外,该问题不是关于输入/输出,而是关于命令行参数。
这种方法几乎完全无效。最终,你必须自己使用ReadConsoleW / WriteConsoleW并从/转换为UTF-16才能获得正确的UTF-8输入/输出。事实证明,Windows必须使用UTF-16才能实现完全的Unicode支持。对于输出,可以使用内部使用WriteConsoleW的libfmt,对于输入,可以使用这里的解决方案:stackoverflow.com/questions/1660492/...
SetConsoleOutputCP(65001)
似乎起作用。