cmd.exe使用什么编码/代码页?


271

在Windows中打开cmd.exe时,它使用什么编码?

如何检查当前使用的编码?它取决于我的区域设置还是有任何环境变量需要检查?

键入具有特定编码的文件时会发生什么?有时我会出现乱码(使用了错误的编码),有时它会起作用。但是,只要我不知道发生了什么,我就什么都不信任。谁能解释?

Answers:


389

是的,这令人沮丧-有时type其他程序会打印乱码,有时却不会。

首先,仅当当前控制台字体包含字符时,才会显示Unicode 字符。因此,请使用Lucida Console之类的TrueType字体代替默认的Raster字体。

但是,如果控制台字体不包含您要显示的字符,您将看到问号而不是乱码。当您出现乱码时,除了字体设置之外,还有更多的事情要做。

当程序使用标准的C库I / O功能(如)时printf程序的输出编码必须与控制台的输出编码匹配,否则您将变得乱码。chcp显示并设置当前代码页。使用标准C库I / O功能的所有输出均被视为,显示在的代码页中chcp

将程序的输出编码与控制台的输出编码进行匹配可以通过两种不同的方式来完成:

  • 程序可以使用chcp或 获取控制台的当前代码页GetConsoleOutputCP,并将其配置为以该编码输出,或者

  • 您或程序可以使用chcpSetConsoleOutputCP匹配程序的默认输出编码来设置控制台的当前代码页。

但是,使用Win32 API的程序可以使用来将UTF-16LE字符串直接写入控制台 WriteConsoleW。这是在不设置代码页的情况下获得正确输出的唯一方法。即使使用该功能,如果字符串开头不是UTF-16LE编码,则Win32程序也必须将正确的代码页传递给 MultiByteToWideChar。同样,WriteConsoleW如果程序的输出被重定向,它将不起作用。在这种情况下,需要更多的摆弄。

type之所以可以工作,是因为它会检查每个文件的开始以获取UTF-16LE 字节顺序标记(BOM),即bytes 0xFF 0xFE。如果找到这样的标记,则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打开一个文件时,它检查的BOM 0xFEFF-即字节 0xFF 0xFE的小端,如果有这样的BOM,type设置内部fOutputUnicode标志。稍后检查此标志以决定是否呼叫WriteConsoleW

但这是获取typeUnicode 的唯一方法,并且仅适用于具有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
…

但是,一个设置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文件,而不管当前的代码页如何
  • 可以使用编程Win32程序以将Unicode输出到控制台 WriteConsoleW
  • 其他设置代码页并相应调整其输出编码的程序可以在控制台上打印Unicode,而与程序启动时的代码页无关
  • 对于其他所有内容,您都必须弄乱chcp,并且可能仍然会得到奇怪的输出。

73
哇,这肯定是我在SO上见过的最详细的答案。出色的印刷品和多国语言的Skillz值得赞扬!太漂亮了,先生!
空袭

2
可能还需要研究VS2008中引入的Microsoft特定扩展_setmode(_fileno(stdout),_O_U16TEXT)。请参见stackoverflow.com/a/9051543stackoverflow.com/a/12015918,以及msdn.microsoft.com/zh-cn/library/tw4k6df8(v=vs.90).aspx除了_setmode()和SetConsoleOutputCP(),这两种方法中还可能隐藏了其他一些细微之处和副作用,乍一看并没有完全了解它们。如果andrewdotn可以用关于_setmode(fd,_O_U16TEXT)的任何观察来更新他的答案,那将是很好的。
JasDev 2014年

13
尽管这是一个很好的答案,但是说控制台支持UTF-16是一种误导。它仅限于UCS-2,即仅限于基本多语言平面(BMP)中的字符。当Win32控制台服务器(现在为conhost.exe)在1990年左右设计时,Unicode是16位标准,因此控制台屏幕缓冲区每个字符单元使用一个16位WCHAR。一个UTF-16代理对打印为两个方框字符。
Eryk Sun

3
@ user200783,不支持分解形式;通常可以转换为NFC等价物。此外,西方语言环境中的控制台不允许混合使用全角和半角字形。同样,当使用代码页65001(UTF-8)时,Windows 8之前的版本会WriteFile报告写入的字符数而不是字节数,因此缓冲的编写器会根据非ASCII字符数重试几次“剩余”字节。同样在65001中,在conhost.exe中读取非ASCII字符失败,因为在调用时,每个UTF-16代码均假定1个ANSI字节WideCharToMultiByte
Eryk Sun

2
此答案中的简单演示程序假定GetStdHandle(STD_OUTPUT_HANDLE)C和C stdout是控制台句柄。在实践中,要测试控制台,请检查是否GetConsoleMode成功。另外,不要使用C运行时_isatty函数来检查低I / O文件描述符是否是控制台。仅检查字符模式设备,其中包括NUL其他设备。而是_get_osfhandle直接调用并检查句柄。
Eryk Sun

29

类型

chcp

查看您当前的代码页(如Dewfy所说)。

nlsinfo

查看所有已安装的代码页,并了解您的代码页号的含义。

您需要安装Windows Server 2003资源工具包(适用于Windows XP)才能使用nlsinfo


19
有趣的是,nlsinfo不会出现在我的Windows 7存在
乔伊

2
nlsinfoWindows XP SP3计算机上也不存在。
Thomas Owens

2
哦,对不起。我认为它带有Windows Server Resource Kit工具。我之前在Windows XP SP3机器上使用过几次,但不知道默认情况下未安装它。
2009年

嗯,这解释了为什么它在我的Vista机器上安装了这些机器的原因。
乔伊(Joey)

4
nlsinfoWindows 10E计算机上也不存在。
Yousha Aleayoub

21

要回答第二个查询。乔尔·斯波斯基(Joel Spolsky)编码如何工作,为此写了一篇很棒的介绍性文章。强力推荐。


13
我已经读过,而且我知道。但是,在Windows上,我总是感到迷lost,因为操作系统和大多数应用程序似乎完全不了解编码。
danglund 2009年

5

命令CHCP显示当前代码页。它具有三位数:8xx,与Windows 12xx不同。因此,输入纯英文文本不会有任何区别,但是扩展的代码页(如西里尔字母)将被错误地打印。


5
CHCP既不显示3位数字,也不显示8 ##格式。437例如是美国编码,它是英语系统上的事实上的标准。-65001是Unicode编码(如果我没记错的话,它是UTF-8,而65000是UTF-7)并且可以选择。例如,CMD还允许切换到1250代码页,但是我不知道,因为何时可以选择这些代码页。(在Win7下。)
Adam LS

4

Windows代码页问题以及由它们引起的C程序可移植性和本地化问题使我很沮丧。先前的文章详细介绍了这些问题,因此在这方面我将不添加任何内容。

长话短说,最终我最终在Visual C ++标准C库上编写了自己的UTF-8兼容性库层。基本上,此库可确保标准C程序在内部使用UTF-8在任何代码页中正常工作。

这个名为MsvcLibX的库可以在https://github.com/JFLarvoire/SysToolsLib中作为开放源代码获得。主要特点:

  • 使用普通char [] C字符串和标准C库API以UTF-8编码的C源。
  • 在任何代码页中,所有内容都在代码中以UTF-8的形式在内部进行处理,包括main()例程argv [],标准输入和输出会自动转换为正确的代码页。
  • 所有stdio.h文件功能都支持UTF-8路径名> 260个字符,实际上最多64 KB。
  • 相同的源代码可以在Windows中使用Visual C ++和MsvcLibX和Visual C ++ C库成功编译并链接,在Linux中使用gcc和Linux标准C库,而无需#ifdef ... #endif块。
  • 添加了Linux中常见但在Visual C ++中缺少的包含文件。例如:unistd.h
  • 添加缺少的功能,例如目录I / O,符号链接管理等功能,所有这些功能当然都支持UTF-8 :-)。

GitHub上MsvcLibX README中有更多详细信息,包括如何构建该库并在您自己的程序中使用它。

上面的GitHub存储库中的发布部分提供了一些使用此MsvcLibX库的程序,这些程序将显示其功能。例:尝试使用我的which.exe工具,在PATH中使用具有非ASCII名称的目录,搜索具有非ASCII名称的程序,并更改代码页。

conv.exe程序是另一个有用的工具。该程序可以轻松地将数据流从任何代码页转换为任何其他代码页。它的默认值是在Windows代码页中输入,在当前控制台代码页中输出。这样可以使用以下简单命令在命令控制台中正确查看Windows GUI应用程序(例如:记事本)生成的数据:type WINFILE.txt | conv

这个MsvcLibX库绝不是完整的,欢迎为改进它而做出的贡献!


2

在Java中,我使用编码“ IBM850”来写入文件。那解决了问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.