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

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

当您以某种编码方式键入文件时会发生什么?有时我得到乱码(不正确的编码使用),有时它的工作。然而,只要我不知道发生了什么,我就不相信任何事情。有人能解释一下吗?


为了回答你的第二个问题,关于编码是如何工作的,Joel Spolsky写了一篇很棒的介绍性文章。强烈推荐。

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

Type

chcp

查看您当前的代码页(正如Dewfy已经说过的)。

Use

nlsinfo

查看所有已安装的代码页,并找出代码页编号的含义。

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

是的,这是令人沮丧的-有时打字和其他程序 印刷上的胡言乱语,有时他们并没有。

首先,Unicode字符只会在 当前控制台字体包含这些字符。所以使用 一个TrueType字体像Lucida控制台代替默认的光栅字体。

但如果控制台字体不包含你想要显示的字符, 你会看到问号而不是胡言乱语。当你出现胡言乱语时, 除了字体设置,还有很多事情要做。

当程序使用标准的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字节顺序标记 (BOM),即字节0xFF 0xFE。 如果它找到这样一个 标记时,它使用WriteConsoleW显示文件中的Unicode字符 不管当前的代码页是什么。但是当输入任何文件时没有 UTF-16LE BOM,或用于在任何命令中使用非ascii字符 不调用writeconsolew -您将需要设置 控制台代码页和程序输出编码相互匹配。


我们怎么才能知道呢?

下面是一个包含Unicode字符的测试文件:

ASCII     abcde xyz
German    äöü ÄÖÜ ß
Polish    ąęźżńł
Russian   абвгдеж эюя
CJK       你好

下面是一个Java程序,可以打印出一系列不同的测试文件 Unicode编码。它可以是任何编程语言;它只打印 ASCII字符或编码字节到标准输出。

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       õ¢áÕÑ¢

唯一有效的是UTF-16LE文件,带有BOM,打印到 控制台通过类型。

如果我们使用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打开一个文件后,它检查0xfeff -即BOM。,字节 在little-endian中,0xFF 0xFE -如果有这样的BOM,则键入设置 internal 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控制台中使用 默认代码页。


对于示例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,并且可能仍然会得到奇怪的输出。

长期以来,我一直为Windows代码页问题以及由此引起的C程序可移植性和本地化问题而感到沮丧。之前的文章已经详细描述了这个问题,所以我不打算在这方面添加任何东西。

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

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

C sources encoded in UTF-8, using normal char[] C strings, and standard C library APIs. In any code page, everything is processed internally as UTF-8 in your code, including the main() routine argv[], with standard input and output automatically converted to the right code page. All stdio.h file functions support UTF-8 pathnames > 260 characters, up to 64 KBytes actually. The same sources can compile and link successfully in Windows using Visual C++ and MsvcLibX and Visual C++ C library, and in Linux using gcc and Linux standard C library, with no need for #ifdef ... #endif blocks. Adds include files common in Linux, but missing in Visual C++. Ex: unistd.h Adds missing functions, like those for directory I/O, symbolic link management, etc, all with UTF-8 support of course :-).

更多细节请参阅GitHub上的MsvcLibX README,包括如何构建库并在您自己的程序中使用它。

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

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

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

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

您可以通过创建一个文件%HOMEPATH%\init.cmd来控制代码页。 我说:

@ECHO OFF
CHCP 65001 > nul