cmd.exe
时,它使用哪种编码?我怎样才能检查它当前正在使用的编码? 它是否取决于我的区域设置或者有没有任何环境变量来检查?
当你用特定的编码键入文件时会发生什么? 有时我得到了乱码字符(因为编码不正确),有时它还能正常工作。 但是,只要我不知道发生了什么事情,我就不相信任何东西。 能否有人解释一下?
cmd.exe
时,它使用哪种编码?是的,有时候type
和其他程序会输出乱码,有时候不会,这让人感到沮丧。
首先,Unicode字符只有在当前控制台字体包含这些字符时才能显示。因此,使用TrueType字体(如Lucida Console)而不是默认的Raster Font。
但是,如果控制台字体不包含您要显示的字符,则会看到问号而不是乱码。当您看到乱码时,除了字体设置之外,还有更多的问题。
当程序使用标准C库I/O函数(如printf
)时,程序的输出编码必须与控制台的输出编码匹配,否则会出现乱码。 chcp
显示并设置当前代码页。所有使用标准C库I/O函数的输出都被视为在chcp
显示的代码页中。
将程序的输出编码与控制台的输出编码匹配可以通过两种不同的方式实现:
程序可以使用chcp
或GetConsoleOutputCP
获取控制台的当前代码页,并将自身配置为以该编码输出,或者
您或程序可以使用chcp
或SetConsoleOutputCP
设置控制台的当前代码页,以匹配程序的默认输出编码。
WriteConsoleW
将UTF-16LE字符串写入控制台。这是在不设置代码页的情况下获得正确输出的唯一方法。即使使用该函数,如果字符串最初不是UTF-16LE编码,则Win32程序必须向MultiByteToWideChar
传递正确的代码页。此外,如果程序的输出被重定向,则WriteConsoleW
将无法正常工作;在这种情况下需要进行更多的操作。
type
命令有时会起作用,因为它检查每个文件的开头是否有 UTF-16LE 字节顺序标记 (BOM),即字节 0xFF 0xFE
。如果发现这样的标记,则无论当前代码页如何,它都将使用 WriteConsoleW
显示文件中的 Unicode 字符。但是,当 type
任何没有 UTF-16LE BOM 的文件或使用任何不调用 WriteConsoleW
的非 ASCII 字符命令时,您需要设置控制台代码页和程序输出编码以匹配。
我该如何找到这个答案呢?
这里有一个包含Unicode字符的测试文件:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ąęźżńł
Russian абвгдеж эюя
CJK 你好
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}
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 õ¢áÕÑ¢
唯一有效的是带有BOM的UTF-16LE文件,通过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打印它。type
命令输出文件时,在调试器中打开 cmd.exe
,我们可以看到这一点。
在打开文件后,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控制台上使用默认代码页打印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
…
#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
输出 Unicode 到控制台。chcp
并且可能仍然会得到奇怪的输出。类型
chcp
如Dewfy所说,要查看当前的代码页。
请使用:
nlsinfo
要查看所有已安装的代码页并了解您的代码页数字的含义。
您需要安装Windows Server 2003 Resource Kit(适用于Windows XP)才能使用nlsinfo
命令。
nlsinfo
。需要翻译请确认。 - Yousha Aleayoub针对您的第二个问题,关于编码如何工作,Joel Spolsky 写了一篇介绍性文章。强烈推荐。
type WINFILE.txt | conv
这个MsvcLibX库并不完整,欢迎贡献以改进它!命令CHCP可以显示当前的代码页。它有三个数字:8xx,与Windows 12xx不同。因此,如果输入纯英文文本,您不会看到任何区别,但是扩展的代码页(如Cyrillic)将被错误地打印出来。
@ECHO OFF
CHCP 65001 > nul
cmd.exe
默认设置为使用ANSI编码免责声明。遵循此处的任何建议都是您自己的风险。
创建并运行一个适当命名的.reg
文件:
1
Windows Registry Editor Version 5.00
;; https://dev59.com/zHM_5IYBdhLWcg3wt1k0#75788701
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
"Autorun"="C:\\Windows\\System32\\chcp.com 1252"
如果您稍后改变主意 - 这里有一个CMD-CodePage-1252-Restore.reg
文件:
Windows Registry Editor Version 5.00
;; https://dev59.com/zHM_5IYBdhLWcg3wt1k0#75788701
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
"Autorun"=-
当我在Windows上打开
cmd.exe
时,它使用的是什么编码?
- 默认情况下,cmd.exe
使用代码页437。
在我看来,这是一个糟糕的选择。
我建议改用您所使用语言的ANSI代码页。
- 它与Microsoft自己的本地文本编辑器C:\WINDOWS\System32\notepad.exe
中的ANSI编码兼容。
对于西欧语言,ANSI表示代码页1252,
或Windows-1252(CP-1252)。
对于其他语言组,我在this answer的末尾发布了一张表格。
如何检查当前正在使用哪种编码?
- 运行C:\WINDOWS\System32\chcp.com
:
C:\>chcp
Active code page: 1252
在我的情况下,它响应1252
而不是437
的原因是,我故意将cmd.exe
默认设置为使用1252。如上面我的“简短”答案所述。
这是否取决于我的区域设置或是否有任何环境变量需要检查?
- 都不是。在这种情况下相关的是语言。
我尝试了以下操作:
WinKey + i > 时间和语言 > 语言 >
首选语言 > 添加语言。
我添加了瑞典语(瑞典),然后确保在
Windows显示语言下选择了瑞典语。
最后,我重新启动了计算机,打开了cmd.exe
,输入了chcp
并按下Enter。
响应是活动代码页:437
。
因此,尽管Windows显示语言更改了Windows的语言,但似乎不影响cmd.exe
使用的代码页。
2
- 是的。这正是您应该期望的。
例如,我有一个名为Some-ANSI-chars.txt
的文件,其中包含用代码页1252编码的瑞典字母å
和ä
,采用ANSI编码。
当我在cmd.exe
中type
文件时,瑞典字母将被正确打印:
C:\stackexchange\stackoverflow\Char-encoding>type Some-ANSI-chars.txt
Sakta men säkert vinner basinkomst mark,
och det viktigaste just nu är att hålla ihop.
C:\stackexchange\stackoverflow\Char-encoding>type Some-UTF-8-chars.txt
Sakta men säkert vinner basinkomst mark,
och det viktigaste just nu är att hålla ihop.
正如您所见,两个UTF-8编码的字符å
和ä
使用两个字节。
type
命令将å
和ä
解码为每个字符两个无意义的单字节字符,即Ã¥
和ä
。对我而言,这不是问题,因为我几乎从不type
我的文本文件内容。
唯一需要考虑的是,我的最爱文本编辑器设置为以UTF-8格式解码我的文件。
3
然而,只要我不知道发生了什么事,我就不相信任何东西。
- 这是明智的选择,它可以保护你(希望如此)免于陷入在cmd.exe
中使用UTF-8编码的陷阱,罪魁祸首是代码页65001。
cmd.exe
中的原因chcp 65001
提供了一些UTF-8解码,但它非常基础且不能提供适当的输入(2016)。chcp 65001
十分危险(2017)。chcp.com 65001
(2019)。如果你迫切需要一个能够正确输出UTF-8编码文件文本的命令行工具,我建议你下载并安装Linux风格的MSYS2,它默认假定你的文本文件是UTF-8编码。
请注意,虽然你的UTF-8字符都被正确呈现:
$ cat Some-UTF-8-chars.txt
Sakta men säkert vinner basinkomst mark,
och det viktigaste just nu är att hålla ihop.
非 ASCII 的 ANSI 字符将被输出为问号:
$ cat Some-ANSI-chars.txt
Sakta men s�kert vinner basinkomst mark,
och det viktigaste just nu �r att h�lla ihop.
cmd.exe
正确输出 ANSI 编码的文件,
4 "Autorun"="chcp 1252>>nul"
chcp 65001
提供了一些UTF-8解码,但非常基础chcp 65001
非常危险chcp.com 65001
1
这个.reg
文件的灵感来自于this answer。
我相信你知道如何在注册表中手动实现相同的事情。
最好先检查一下注册表,看看是否已经有一个名为Autorun
的REG_SZ值。
这个注册表修改不会影响PowerShell。- 打开PowerShell并运行chcp
。期望看到Active code page: 437
。
当然,我在我的.bat
文件中也使用代码页1252。
大约99%的文件都是纯ASCII文件。
2
在进行这个实验时,我确保在注册表中的HKLM\SOFTWARE\Microsoft\Command Processor
下没有Autorun
值。
3
准确地说,我至少有三个“喜爱”的文本编辑器,
Notepad2, Notepad++,
和Visual Studio Code。
这三个编辑器中,Visual Studio Code设置为通过UTF-8编码所有文件,而Notepad2和Notepad++
自动检测编辑器认为正确的编码。
4 只要你采用了我“简短答案”中的注册表修改。
WriteFile
报告写入的字符数而不是字节数,因此带缓冲的写入器会根据非ASCII字符的数量多次重试“剩余”的字节。在65001中,由于在调用WideCharToMultiByte
时假定每个UTF-16代码有1个ANSI字节,因此在conhost.exe中读取非ASCII字符会失败。 - Eryk SunGetStdHandle(STD_OUTPUT_HANDLE)
和 Cstdout
是控制台句柄。在实践中,要测试是否为控制台,请检查GetConsoleMode
是否成功。同时,不要使用 C 运行时的_isatty
函数来检查低 I/O 文件描述符是否是控制台;那只是检查字符模式设备,其中包括了NUL
等其他内容。相反,调用_get_osfhandle
并直接检查句柄。 - Eryk Sun