当我在Windows中打开cmd.exe时,它使用的是什么编码?
如何查看当前使用的编码?它取决于我的区域设置还是有任何环境变量需要检查?
使用特定编码键入文件时会发生什么?有时我会收到乱码(使用的编码不正确),有时候它会有所作为。但是,只要我不知道发生了什么,我就不相信任何事情。谁能解释一下?
答案 0 :(得分:364)
是的,令人沮丧的是 - 有时type
和其他程序
打印乱码,有时他们没有。
首先,Unicode字符只显示if the current console font contains the characters。所以使用 一个TrueType字体,如Lucida Console,而不是默认的Raster字体。
但是如果控制台字体不包含您要显示的字符, 你会看到问号而不是胡言乱语。当你得到胡言乱语时 除了字体设置外,还有更多功能。
当程序使用标准C库I / O函数时,如printf
,
程序的输出编码必须与控制台的输出编码匹配,或
你会得到胡言乱语。 chcp
显示并设置当前代码页。所有
使用标准C库I / O函数的输出被视为在其中
代码页由chcp
显示。
将程序的输出编码与控制台的输出编码相匹配 可以通过两种不同的方式完成:
程序可以使用chcp
或来获取控制台的当前代码页
GetConsoleOutputCP
,并将自身配置为以该编码输出,或
您或程序可以使用chcp
或设置控制台的当前代码页
SetConsoleOutputCP
以匹配程序的默认输出编码。
但是,使用Win32 API的程序可以直接编写UTF-16LE字符串
到控制台
WriteConsoleW
。
这是在不设置代码页的情况下获得正确输出的唯一方法。和
即使使用该函数,如果字符串不是UTF-16LE编码
首先,Win32程序必须将正确的代码页传递给
MultiByteToWideChar
。
此外,如果程序的输出被重定向,WriteConsoleW
将不起作用;
在这种情况下需要更多的摆弄。
type
在某些时候有效,因为它会检查每个文件的开头
UTF-16LE Byte Order Mark
(BOM),即字节0xFF 0xFE
。
如果它找到这样的
mark,它使用WriteConsoleW
在文件中显示Unicode字符
无论当前的代码页如何。但是当type
任何文件没有时
UTF-16LE BOM,或用于任何命令的非ASCII字符
不会调用WriteConsoleW
- 你需要设置
控制台代码页和程序输出编码相互匹配。
我们怎样才能找到这个?
这是一个包含Unicode字符的测试文件:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
这是一个Java程序,用于打印出一堆不同的测试文件
Unicode编码。它可以是任何编程语言;它只打印
ASCII字符或编码字节到stdout
。
import java.io.*;
public class Foo {
private static final String BOM = "\ufeff";
private static final String TEST_STRING
= "ASCII abcde xyz\n"
+ "German äöü ÄÖÜ ß\n"
+ "Polish ąęźżńł\n"
+ "Russian абвгдеж эюя\n"
+ "CJK 你好\n";
public static void main(String[] args)
throws Exception
{
String[] encodings = new String[] {
"UTF-8", "UTF-16LE", "UTF-16BE", "UTF-32LE", "UTF-32BE" };
for (String encoding: encodings) {
System.out.println("== " + encoding);
for (boolean writeBom: new Boolean[] {false, true}) {
System.out.println(writeBom ? "= bom" : "= no bom");
String output = (writeBom ? BOM : "") + TEST_STRING;
byte[] bytes = output.getBytes(encoding);
System.out.write(bytes);
FileOutputStream out = new FileOutputStream("uc-test-"
+ encoding + (writeBom ? "-bom.txt" : "-nobom.txt"));
out.write(bytes);
out.close();
}
}
}
}
默认代码页中的输出? 总垃圾!
Z:\andrew\projects\sx\1259084>chcp
Active code page: 850
Z:\andrew\projects\sx\1259084>java Foo
== UTF-8
= no bom
ASCII abcde xyz
German ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish ąęźżńł
Russian ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK õ¢áÕÑ¢
= bom
´╗┐ASCII abcde xyz
German ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish ąęźżńł
Russian ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK õ¢áÕÑ¢
== UTF-16LE
= no bom
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ♣☺↓☺z☺|☺D☺B☺
R u s s i a n 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦
C J K `O}Y
= bom
■A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ♣☺↓☺z☺|☺D☺B☺
R u s s i a n 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦
C J K `O}Y
== UTF-16BE
= no bom
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣☺↓☺z☺|☺D☺B
R u s s i a n ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O
C J K O`Y}
= bom
■ A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣☺↓☺z☺|☺D☺B
R u s s i a n ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O
C J K O`Y}
== UTF-32LE
= no bom
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ♣☺ ↓☺ z☺ |☺ D☺ B☺
R u s s i a n 0♦ 1♦ 2♦ 3♦ 4♦ 5♦ 6♦ M♦ N
♦ O♦
C J K `O }Y
= bom
■ A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ♣☺ ↓☺ z☺ |☺ D☺ B☺
R u s s i a n 0♦ 1♦ 2♦ 3♦ 4♦ 5♦ 6♦ M♦ N
♦ O♦
C J K `O }Y
== UTF-32BE
= no bom
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣ ☺↓ ☺z ☺| ☺D ☺B
R u s s i a n ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N
♦O
C J K O` Y}
= bom
■ A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣ ☺↓ ☺z ☺| ☺D ☺B
R u s s i a n ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N
♦O
C J K O` Y}
但是,如果我们type
保存了哪些文件怎么办?它们包含确切的
打印到控制台的相同字节。
Z:\andrew\projects\sx\1259084>type *.txt
uc-test-UTF-16BE-bom.txt
■ A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣☺↓☺z☺|☺D☺B
R u s s i a n ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O
C J K O`Y}
uc-test-UTF-16BE-nobom.txt
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣☺↓☺z☺|☺D☺B
R u s s i a n ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O
C J K O`Y}
uc-test-UTF-16LE-bom.txt
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
uc-test-UTF-16LE-nobom.txt
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ♣☺↓☺z☺|☺D☺B☺
R u s s i a n 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦
C J K `O}Y
uc-test-UTF-32BE-bom.txt
■ A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣ ☺↓ ☺z ☺| ☺D ☺B
R u s s i a n ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N
♦O
C J K O` Y}
uc-test-UTF-32BE-nobom.txt
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ☺♣ ☺↓ ☺z ☺| ☺D ☺B
R u s s i a n ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N
♦O
C J K O` Y}
uc-test-UTF-32LE-bom.txt
A S C I I a b c d e x y z
G e r m a n ä ö ü Ä Ö Ü ß
P o l i s h ą ę ź ż ń ł
R u s s i a n а б в г д е ж э ю я
C J K 你 好
uc-test-UTF-32LE-nobom.txt
A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ♣☺ ↓☺ z☺ |☺ D☺ B☺
R u s s i a n 0♦ 1♦ 2♦ 3♦ 4♦ 5♦ 6♦ M♦ N
♦ O♦
C J K `O }Y
uc-test-UTF-8-bom.txt
´╗┐ASCII abcde xyz
German ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish ąęźżńł
Russian ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK õ¢áÕÑ¢
uc-test-UTF-8-nobom.txt
ASCII abcde xyz
German ├ñ├Â├╝ ├ä├û├£ ├ƒ
Polish ąęźżńł
Russian ð░ð▒ð▓ð│ð┤ðÁð ÐìÐÄÐÅ
CJK õ¢áÕÑ¢
唯一的工作原理是UTF-16LE文件,带有BOM,打印到
通过type
控制台。
如果我们使用type
以外的任何内容来打印文件,我们就会得到垃圾:
Z:\andrew\projects\sx\1259084>copy uc-test-UTF-16LE-bom.txt CON
■A S C I I a b c d e x y z
G e r m a n õ ÷ ³ ─ Í ▄ ▀
P o l i s h ♣☺↓☺z☺|☺D☺B☺
R u s s i a n 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦
C J K `O}Y
1 file(s) copied.
由于copy CON
无法正确显示Unicode,我们可以
得出结论,type
命令具有检测UTF-16LE BOM的逻辑
启动该文件,并使用特殊的Windows API进行打印。
我们可以通过在cmd.exe
调试器中打开type
来看到这一点
出文件:
在type
打开文件后,它会检查0xFEFF
的BOM - 即字节数
小端的0xFF 0xFE
- 如果有这样的BOM,type
设置了一个
内部fOutputUnicode
标志。稍后检查此标志以进行确定
是否致电WriteConsoleW
。
但这是让type
输出Unicode的唯一方法,仅适用于文件
有BOM并且是UTF-16LE。对于所有其他文件和程序
没有特殊代码来处理控制台输出,你的文件将是
根据当前代码页解释,并可能显示为
乱码。
您可以模拟type
如何在您自己的程序中将Unicode输出到控制台,如下所示:
#include <stdio.h>
#define UNICODE
#include <windows.h>
static LPCSTR lpcsTest =
"ASCII abcde xyz\n"
"German äöü ÄÖÜ ß\n"
"Polish ąęźżńł\n"
"Russian абвгдеж эюя\n"
"CJK 你好\n";
int main() {
int n;
wchar_t buf[1024];
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
n = MultiByteToWideChar(CP_UTF8, 0,
lpcsTest, strlen(lpcsTest),
buf, sizeof(buf));
WriteConsole(hConsole, buf, n, &n, NULL);
return 0;
}
此程序适用于使用Windows在Windows控制台上打印Unicode 默认代码页。
对于示例Java程序,我们可以得到一些正确的输出 手动设置代码页,虽然输出以奇怪的方式搞砸了:
Z:\andrew\projects\sx\1259084>chcp 65001
Active code page: 65001
Z:\andrew\projects\sx\1259084>java Foo
== UTF-8
= no bom
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
ж эюя
CJK 你好
你好
好
�
= bom
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
еж эюя
CJK 你好
你好
好
�
== UTF-16LE
= no bom
A S C I I a b c d e x y z
…
但是,一个设置Unicode UTF-8代码页的C程序:
#include <stdio.h>
#include <windows.h>
int main() {
int c, n;
UINT oldCodePage;
char buf[1024];
oldCodePage = GetConsoleOutputCP();
if (!SetConsoleOutputCP(65001)) {
printf("error\n");
}
freopen("uc-test-UTF-8-nobom.txt", "rb", stdin);
n = fread(buf, sizeof(buf[0]), sizeof(buf), stdin);
fwrite(buf, sizeof(buf[0]), n, stdout);
SetConsoleOutputCP(oldCodePage);
return 0;
}
确实有正确的输出:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
故事的寓意?
type
可以打印带有BOM的UTF-16LE文件,无论您当前的代码页如何WriteConsoleW
。chcp
混乱,并可能仍然会得到奇怪的输出。答案 1 :(得分:27)
类型
chcp
查看您当前的代码页(正如Dewfy已经说过的那样)。
使用
nlsinfo
查看所有已安装的代码页,并找出代码页编号的含义。
您需要安装Windows Server 2003资源工具包(适用于Windows XP)才能使用nlsinfo
。
答案 2 :(得分:21)
回答你的第二个问题。编码是如何运作的,Joel Spolsky写了一篇很棒的introductory article on this。强烈推荐。
答案 3 :(得分:5)
命令CHCP显示当前代码页。它有三个数字:8xx,与Windows 12xx不同。因此,键入一个只有英文的文本,您将看不到任何差异,但会错误地打印扩展代码页(如Cyrillic)。
答案 4 :(得分:3)
我长期以来因Windows代码页问题以及它们导致的C程序可移植性和本地化问题而感到沮丧。之前的帖子已详细详述了这些问题,因此我不打算在这方面添加任何内容。
总而言之,最终我最终在Visual C ++标准C库上编写了自己的UTF-8兼容性库层。基本上,这个库确保标准C程序在任何代码页中都可以正常使用UTF-8。
这个名为MsvcLibX的库在https://github.com/JFLarvoire/SysToolsLib以开源形式提供。主要特点:
MsvcLibX README on GitHub中的更多详细信息,包括如何构建库并在您自己的程序中使用它。
上述GitHub存储库中的release section使用此MsvcLibX库提供了几个程序,这些程序将显示其功能。例如:在PATH中尝试使用带有非ASCII名称的目录的who.exe工具,搜索具有非ASCII名称的程序,以及更改代码页。
另一个有用的工具是conv.exe程序。该程序可以轻松地将数据流从任何代码页转换为任何其他代码页。它的默认值是在Windows代码页中输入,并在当前控制台代码页中输出。这允许在命令控制台中使用以下简单命令正确查看Windows GUI应用程序(例如:记事本)生成的数据:type WINFILE.txt | conv
这个MsvcLibX库并不完整,欢迎为改进它做出贡献!
答案 5 :(得分:0)
在Java中,我使用编码“ IBM850”写入文件。那解决了问题。