cmd.exe使用什么编码/代码页?
当我在Windows中打开cmd.exe时,它使用什么编码?
如何检查当前使用哪种编码? 这取决于我的区域设置还是有任何环境variables要检查?
当你input一个特定的编码文件会发生什么? 有时候我会遇到乱码(使用不正确的编码),有时会出现这种情况。 但是,只要我不知道发生了什么,我就不会相信任何事情。 谁能解释一下?
是的,这是令人沮丧的 – 有时type
和其他程序打印乱码,有时他们没有。
首先,只有当前控制台字体包含字符时才会显示Unicode 字符 。 因此,请使用像Lucida控制台这样的TrueType字体,而不是默认的光栅字体。
但是,如果控制台字体不包含要显示的字符,则会看到问号而不是乱码。 当你乱七八糟的时候,还有更多的事情不只是字体设置。
当程序使用标准的C库I / O函数(如printf
, 程序的输出编码必须与控制台的输出编码相匹配 ,否则会得到乱码。 chcp
显示并设置当前的代码页。 所有使用标准C库I / O函数的输出都被视为处于chcp
显示的代码页中。
程序的输出编码与控制台的输出编码匹配可以通过两种不同的方式完成:
-
程序可以使用
chcp
或GetConsoleOutputCP
获取控制台的当前代码页,并将其自身configuration为以该编码输出,或 -
您或程序可以使用
chcp
或SetConsoleOutputCP
来设置控制台的当前代码页以匹配程序的默认输出编码。
但是,使用Win32 API的程序可以使用WriteConsoleW
将UTF-16LEstring直接写入控制台。 这是在没有设置代码页的情况下获得正确输出的唯一方法。 即使使用该函数,如果一个string不是以UTF-16LE编码开始的,Win32程序必须将正确的代码页传递给MultiByteToWideChar
。 另外,如果程序的输出被redirect, WriteConsoleW
将不起作用。 在这种情况下需要更多的手段。
type
工作,因为它检查每个文件的开始UTF-16LE 字节顺序标记(BOM) ,即字节0xFF 0xFE
。 如果find这样的标记,则不pipe当前的代码页如何,都使用WriteConsoleW
在文件中显示Unicode字符。 但是,当type
任何没有UTF-16LE BOM的文件,或者使用任何不调用WriteConsoleW
命令使用非ASCII字符时,您需要设置控制台代码页和程序输出编码以相互匹配。
我们怎么能find这个?
这是一个包含Unicode字符的testing文件:
ASCII abcde xyz German äöü ÄÖÜ ß Polish ąęźżńł Russian абвгдеж эюя CJK 你好
这里有一个Java程序,用一堆不同的Unicode编码打印出testing文件。 它可以用任何编程语言; 它只打印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 ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ♣☺↓☺z☺|☺D☺B☺ R ussian 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦ CJK `O}Y = bom ■ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ♣☺↓☺z☺|☺D☺B☺ R ussian 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦ CJK `O}Y == UTF-16BE = no bom ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣☺↓☺z☺|☺D☺B R ussian ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O CJKO`Y} = bom ■ ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣☺↓☺z☺|☺D☺B R ussian ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O CJKO`Y} == UTF-32LE = no bom ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ♣☺ ↓☺ z☺ |☺ D☺ B☺ R ussian 0♦ 1♦ 2♦ 3♦ 4♦ 5♦ 6♦ M♦ N ♦ O♦ CJK `O }Y = bom ■ ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ♣☺ ↓☺ z☺ |☺ D☺ B☺ R ussian 0♦ 1♦ 2♦ 3♦ 4♦ 5♦ 6♦ M♦ N ♦ O♦ CJK `O }Y == UTF-32BE = no bom ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣ ☺↓ ☺z ☺| ☺D ☺B R ussian ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N ♦O CJKO` Y} = bom ■ ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣ ☺↓ ☺z ☺| ☺D ☺B R ussian ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N ♦O CJKO` Y}
但是,如果我们type
保存的文件呢? 它们包含打印到控制台的完全相同的字节。
Z:\andrew\projects\sx\1259084>type *.txt uc-test-UTF-16BE-bom.txt ■ ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣☺↓☺z☺|☺D☺B R ussian ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O CJKO`Y} uc-test-UTF-16BE-nobom.txt ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣☺↓☺z☺|☺D☺B R ussian ♦0♦1♦2♦3♦4♦5♦6 ♦M♦N♦O CJKO`Y} uc-test-UTF-16LE-bom.txt ASCII abcde xyz German äöü ÄÖÜ ß Polish ąęźżńł Russian абвгдеж эюя CJK 你好uc-test-UTF-16LE-nobom.txt ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ♣☺↓☺z☺|☺D☺B☺ R ussian 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦ CJK `O}Y uc-test-UTF-32BE-bom.txt ■ ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣ ☺↓ ☺z ☺| ☺D ☺B R ussian ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N ♦O CJKO` Y} uc-test-UTF-32BE-nobom.txt ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ☺♣ ☺↓ ☺z ☺| ☺D ☺B R ussian ♦0 ♦1 ♦2 ♦3 ♦4 ♦5 ♦6 ♦M ♦N ♦O CJKO` Y} uc-test-UTF-32LE-bom.txt ASCII abcdexyz G erman ä ö ü Ä Ö Ü ß P olish ą ę ź ż ń ł R ussian а б в г д е ж э ю я CJK 你 好uc-test-UTF-32LE-nobom.txt ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ♣☺ ↓☺ z☺ |☺ D☺ B☺ R ussian 0♦ 1♦ 2♦ 3♦ 4♦ 5♦ 6♦ M♦ N ♦ O♦ CJK `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 ■ASCII abcdexyz G erman õ ÷ ³ ─ Í ▄ ▀ P olish ♣☺↓☺z☺|☺D☺B☺ R ussian 0♦1♦2♦3♦4♦5♦6♦ M♦N♦O♦ CJK `O}Y 1 file(s) copied.
从copy CON
没有正确显示Unicode这一事实,我们可以得出结论: type
命令具有检测文件开始处的UTF-16LE BOM的逻辑,并使用特殊的Windows API来打印它。
我们可以通过在debugging器中打开cmd.exe
来type
文件:
type
打开文件后,它会检查0xFEFF
-ie的BOM,little-endian中的字节0xFF 0xFE
,如果有这样的BOM,则type
设置一个内部fOutputUnicode
标志。 稍后检查此标志以决定是否调用WriteConsoleW
。
但这是获取type
以输出Unicode的唯一方法,并且仅限于具有BOM和UTF-16LE的文件。 对于所有其他文件,对于没有特殊代码来处理控制台输出的程序,您的文件将根据当前的代码页进行解释,并可能显示为乱码。
您可以模拟如何在自己的程序中输出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程序,通过手动设置代码页,我们可以得到一点正确的输出,尽pipe输出会以奇怪的方式搞乱:
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 ASCII abcdexyz …
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 ASCII abcdexyz …
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 ASCII abcdexyz …
但是,一个设置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程序可以编程为使用
WriteConsoleW
将Unicode输出到控制台。 - 其他程序设置代码页并相应地调整其输出编码,可以在控制台上打印Unicode,而不pipe程序启动时的代码页是什么
- 对于其他所有的东西,你将不得不使用
chcp
,而且可能还会得到奇怪的输出。
要回答你的第二个查询重新。 编码是如何工作的,Joel Spolsky 就此写了一篇很好的介绍性文章 。 powershell推荐。
types
chcp
看到你目前的代码页(如Dewfy已经说过)。
使用
nlsinfo
看到所有已安装的代码页,并找出你的代码页号码的含义。
您需要安装Windows Server 2003 Resource Kit(适用于Windows XP)以使用nlsinfo
。
命令CHCP显示当前的代码页。 它有三个数字:8xx和Windows 12xx不同。 因此,input一个纯英文文本,你不会看到任何区别,但是一个扩展的代码页(如西里尔字母)将被打印错误。
由于Windows代码页问题,以及C程序的可移植性和本地化问题,我一直感到沮丧。 以前的post已经详细列出了这些问题,所以我不打算在这方面添加任何内容。
简而言之,最终我在Visual C ++标准C库上编写了自己的UTF-8兼容库。 基本上这个库确保了一个标准的C程序在任何代码页中都能正常工作,在内部使用UTF-8。
这个名为MsvcLibX的库可以在https://github.com/JFLarvoire/SysToolsLib以开放源代码的forms获得。; 主要特点:
- 以UTF-8编码的C源代码,使用正常的char [] Cstring和标准C库API。
- 在任何代码页中,所有内容在代码中都以UTF-8的forms进行处理,包括main()例程argv [],标准input和输出会自动转换为正确的代码页。
- 所有stdio.h文件函数都支持> 260个字符的UTF-8path名,实际上最多可达64 KB。
- 使用Visual C ++和MsvcLibX以及Visual C ++ C库,在Windows下使用gcc和Linux标准C库,可以在Windows中编译和链接相同的源代码,而不需要#ifdef … #endif块。
- 添加Linux中常见的包含文件,但在Visual C ++中缺失。 例如:unistd.h
- 添加丢失的函数,如目录I / O,符号链接pipe理等等,都支持UTF-8 :-)。
有关GitHub上MsvcLibX README的更多详细信息,包括如何构build库并在您自己的程序中使用它。
上面的GitHub仓库中的release部分提供了几个使用这个MsvcLibX库的程序,它将显示它的function。 例如:在PATH中尝试使用带有非ASCII名称的目录的which.exe工具,search具有非ASCII名称的程序以及更改代码页。
另一个有用的工具是conv.exe程序。 这个程序可以很容易地将数据stream从任何代码页转换为任何其他。 它的默认值是在Windows代码页input的,并输出到当前的控制台代码页中。 这允许在命令控制台中正确查看由Windows GUI应用程序(例如:记事本)生成的数据,只需使用以下简单的命令: type WINFILE.txt | conv
type WINFILE.txt | conv
这个MsvcLibX库并不完整,欢迎提供改进意见!