[C++] 现在到底怎样在 window cmd 或 poweshell 输出 utf8 编码的(中文)字符啊?

2018-06-06 22:29:14 +08:00
 rebeccaMyKid

chcp 65001 我用不了,而且也好像说不好。

https://stackoverflow.com/questions/388490/how-to-use-unicode-characters-in-windows-command-line/388500#388500

这个 accepted answer 说的什么我根本看不懂。。有前辈能解释一下吗?

1974 次点击
所在节点    问与答
16 条回复
alvin666
2018-06-06 22:33:40 +08:00
为什么用不了?什么 win 版本? chcp 是换代码页的,直接换了啊,谁说不好???
rebeccaMyKid
2018-06-06 22:38:01 +08:00
@alvin666
win10。我换了没用。
不少 comment 和回答提到都说不好:
"chcp 65001 is very dangerous. Unless a program was specially designed to work around defects in the Microsoft ’ s C runtime library (or uses a different CRTL), it would not work reliably."

"Note there are serious implementation bugs in Windows's code page 65001 support which will break many applications that rely on the C standard library IO methods, so this is very fragile. (Batch files also just stop working in 65001.) Unfortunately UTF-8 is a second-class citizen in Windows."

"My understanding is that calls that return a number-of-bytes (such as fread/fwrite/etc) actually return a number-of-characters. This causes a wide variety of symptoms, such as incomplete input-reading, hangs in fflush, the broken batch files and so on. Some background. The default code pages used for CJK "multibyte" locales have special handling built in to fix this, but 65001 doesn't - it is not supported."

具体意思我看不太懂
alvin666
2018-06-06 22:42:59 +08:00
危险就是如果你的 bat 运行在标准的 c 库上,i/o 的时候可能出问题,你的需求是什么?别看见 dangerous 就觉得不能用,他的解决方案是不换代码页,输出 Unicode,转 utf8,不知道你的需求是什么。大不了输出 Unicode 自己转
还有,这个英语很好懂,业余时间可以自己学一下,或者用用谷歌翻译
Cambrian07
2018-06-06 22:49:00 +08:00
我记得 win10 最新更新之后 ps 支持默认 UTF-8 了
rebeccaMyKid
2018-06-06 22:50:17 +08:00
@alvin666
因为我不知道什么时候会关联到 i/o,所以我想知道有没有别的方法。而且这个 chcp65001 在我电脑的确不行。。那个最高票的 answer 也有一个 comment 说是试了不行的。。

英语我还行,就是我不太懂他说的什么 IO library,因为不知道能牵扯到什么地方。
总之还是谢谢回帖。
msg7086
2018-06-06 23:35:02 +08:00
本来直接 CHCP 就不好,因为 Windows 下很多的设计都没有考虑到 UTF-8 的问题。

所以,就不要折腾 UTF-8 了,老老实实 GBK 吧。
geelaw
2018-06-06 23:40:54 +08:00
我没有理解您在 C++ 里的操作和 cmd/powershell 有什么关系。

如果你想让你的 C++ 代码使用 UTF-8 编码,用 cl /utf-8 编译。

如果你希望控制台接受一个特定编码的字节流,使用 chcp <code-page>。对于 cmd,当你不用重定向、管道的时候,程序向标准输出、标准错误输出的字节流会原样进入控制台。

对于 PowerShell 情况会比较复杂,因为是面向对象的 shell。分为以下两种情况:

- 如果是 <exe-name> <arg1> <arg2> ... 的形式且没有任何重定向、管道,则程序向标准输出、标准错误输出的字节流原样进入控制台;
- 如果命令是表达式的一部分,则 PowerShell 会猜测标准输出的编码,尝试把它变成一个字符串。

在后一种情况下,如果再把字符串 Write-Host 输出(或者 Write-Object 但是没有用管道、重定向),则 PowerShell 会输出那个字符串—— PowerShell 使用的是 Unicode,所以输出和编码没有关系。

下面的图展示了几种情况。

代码:UTF-8 编码的。



情况 1:C++ 程序输出的是 GBK,控制台代码页是 UTF-8,使用 cmd。这时程序输出的字节流直接进入控制台,也就是说会按照 UTF-8 解读 GBK 编码的文本,会是乱码。



情况 2:C++ 程序输出的是 GBK,控制台的代码页是 GBK,使用 cmd。这时程序输出的字节流直接进入控制台,也就是说会按照 GBK 解读 GBK 编码的文本,会是正确的字符。



情况 3:C++ 程序输出的是 GBK,控制台的代码页是 UTF-8,使用 cmd。这时程序输出的字节流直接进入控制台,也就是说会按照 GBK 解读 UTF-8 编码的文本,会是另外一种乱码。



情况 4:C++ 程序输出的是 UTF-8,控制台的代码页是 UTF-8,使用 cmd。这时程序输出的字节流直接进入控制台,也就是说会按照 UTF-8 解读 UTF-8 编码的文本,会是正确的字符。



情况 5、6:C++ 程序输出的是 UTF-8,控制台的代码页是 UTF-8,当前系统 locale 是 2052 (就是 zh-CN,对应默认代码页 936 = GBK ),使用 PowerShell。分别用 外部程序是表达式的一部分 和 单纯启动外部程序 的语法。前者,PowerShell 需要把程序的标准输出变成字符串,程序不是 Unicode 输出,因此 PowerShell 决定使用默认代码页,也就是 936,去解读字节流( UTF-8 ),得到的字符串(存储为 Unicode )是乱码 [相当于 字符串 = MultiByteToUnicode(标准输出, locale 对应的编码)] ,再输出这个字符串,自然也是乱码;后者,PowerShell 让程序的标准输出字节流原样直接进入自己的控制台,因此是 UTF-8 解读 UTF-8,看到的是正确的字符。



情况 7、8:C++ 程序输出的是 UTF-8,控制台的代码页是 GBK,系统默认代码页 GBK,PowerShell,分别用 表达式 和 单纯启动 语法。无论怎样都会用 GBK 解读 UTF-8,因此是一样的乱码。




总结:假设系统 locale 设置的默认代码页是 CPSysLocale,则默认控制台的代码页也是 CPSysLocale,但你可以用 chcp 改变,假设你改成了 CPConsole,最后,假设你的 C++ 程序输出多字节字符串,使用的代码页是 CPProg。

如果用 cmd + 重定向 /管道,则你的输出按原字节进入重定向 /管道目标,和 console 没有关系。

如果用 cmd 输出到控制台,你的输出按原字节进入控制台,CPProg 输出将按照 CPConsole 被解读并显示。

如果用 PowerShell 表达式语法,则你的 CPProg 输出将被 PowerShell 猜测编码,如果它没有 BOM,则会按照 CPSysLocale 解读,变换为 .NET 字符串;再次输出时,输出的是变换后的字符串,因为字符串已经是 Unicode,不会涉及到 CPConsole ;结果就是:CPProg 按照 CPSysLocale 解读。

如果用 PowerShell 单纯启动语法,你的输出按原字节进入控制台,CPProg 输出将按照 CPConsole 被解读并显示。

注意:自 1803 起,你可以设置 UTF-8 (65001) 为 locale 设置的默认代码页,但在 1709 及更早,不行。此外,PowerShell 的表达式语法包括任何有重定向的情况,而且 PowerShell 会帮你分行,但再次输出的时候原来的行尾信息已经丢失,因此用 PowerShell 直接调用外部命令是一个非常非常糟糕的做法。


安利:如果你要小心地操作文件编码,请使用 cmd 重定向+自己转换编码;或者你可以用我的 Use-RawPipeline PowerShell module。见这里: https://github.com/GeeLaw/PowerShellThingies/tree/master/modules/Use-RawPipeline
rebeccaMyKid
2018-06-07 00:24:57 +08:00
@geelaw
太厉害了。
前面的尝试我几乎都做过,就是 chcp 65001 不行,看了一下我的系统版本,1709。Impressive answer. 十分感谢。

后面有些管道的还不太懂,不过没关系,已经够了。谢啦
msg7086
2018-06-07 00:29:07 +08:00
所以说 Windows 下折腾 UTF-8 真的是非常折腾。
我写脚本输出字符集全靠猜,蒙对了就不管了,基本就是这样……
rebeccaMyKid
2018-06-07 00:38:45 +08:00
@msg7086 前辈心大啊…我现在还是决定用 Git Bash 来看输出。就是我之前用 MacOS 过来的,很不爽,
就死磕上了,结果把自己磕惨了,不过也算得到结果了哈哈。
gnaggnoyil
2018-06-07 06:14:21 +08:00
@msg7086 C/C++在语言和运行时层面不区分 byte 和 char 那能怪巨硬吗…… Linux 下不也就是有个 UTF-8 作为 de facto standard 而已……在那之前不还是 ANSI 一把梭.还记得 Shift_JIS 的 5c 问题吗.
msg7086
2018-06-07 06:39:11 +08:00
@gnaggnoyil 也没有怪啊。
cyspy
2018-06-07 07:48:56 +08:00
最新版可以切换到全局 UTF8
lsmgeb89
2018-06-07 09:33:38 +08:00
@cyspy 可以全局 utf-8 那真是省事多了
rebeccaMyKid
2018-06-07 09:49:21 +08:00
@lsmgeb89
那是个 beta,好像有人说有些旧软件会出错。我昨晚更新完到 1803 版本出现了这个选项,我没用。
但目前我这个版本 chcp65001 依旧不行。
rebeccaMyKid
2018-06-07 09:51:13 +08:00
而且我感觉肯定不能默认 utf8 来开发的,那人家 win7,没设置这个全局 utf8 的 win10 就不用用你的了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/461023

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX