dan*_*und 258 windows encoding command-line
当我在Windows中打开cmd.exe时,它使用的是什么编码?
如何查看当前使用的编码?它取决于我的区域设置还是有任何环境变量需要检查?
键入具有特定编码的文件时会发生什么?有时我会收到乱码(使用的编码不正确),有时候它会有所作为.但是,只要我不知道发生了什么,我就不相信任何事情.谁能解释一下?
and*_*otn 376
是的,这令人沮丧 - 有时候type
,其他程序打印出胡言乱语,有时他们却没有.
首先,只有当前控制台字体包含字符时才会显示Unicode 字符.因此,使用像Lucida Console这样的TrueType字体而不是默认的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 字节顺序标记(BOM),即字节0xFF 0xFE
.如果找到这样的标记,则WriteConsoleW
无论当前代码页如何,它都会在文件中显示Unicode字符.但是当type
没有UTF-16LE BOM的任何文件,或者使用任何不调用的命令的非ASCII字符时,WriteConsoleW
你需要设置控制台代码页和程序输出编码以便相互匹配.
我们怎么能找到这个呢?
这是一个包含Unicode字符的测试文件:
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ??????
Russian ??????? ???
CJK ??
Run Code Online (Sandbox Code Playgroud)
这是一个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();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
默认代码页中的输出?总垃圾!
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}
Run Code Online (Sandbox Code Playgroud)
但是,如果我们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 õ¢áÕÑ¢
Run Code Online (Sandbox Code Playgroud)
该唯一的作品就是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.
Run Code Online (Sandbox Code Playgroud)
copy CON
从未正确显示Unicode 的事实来看,我们可以得出结论,该type
命令具有在文件开头检测UTF-16LE BOM的逻辑,并使用特殊的Windows API来打印它.
我们可以通过cmd.exe
在调试器中打开type
文件时打开它来看到:
之后type
打开一个文件,它会检查的BOM 0xFEFF
-即字节
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;
}
Run Code Online (Sandbox Code Playgroud)
此程序适用于使用默认代码页在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
…
Run Code Online (Sandbox Code Playgroud)
但是,一个设置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;
}
Run Code Online (Sandbox Code Playgroud)
确实有正确的输出:
Z:\andrew\projects\sx\1259084>.\test
ASCII abcde xyz
German äöü ÄÖÜ ß
Polish ??????
Russian ??????? ???
CJK ??
Run Code Online (Sandbox Code Playgroud)
这个故事的主旨?
type
无论您当前的代码页如何,都可以使用BOM打印UTF-16LE文件WriteConsoleW
.chcp
,并可能仍然会得到奇怪的输出.Cag*_*aya 27
类型
chcp
Run Code Online (Sandbox Code Playgroud)
查看您当前的代码页(正如Dewfy已经说过的那样).
使用
nlsinfo
Run Code Online (Sandbox Code Playgroud)
查看所有已安装的代码页并找出代码页编号的含义.
您需要安装Windows Server 2003资源工具包(适用于Windows XP)才能使用nlsinfo
.
长期以来,我一直对 Windows 代码页问题以及它们引起的 C 程序可移植性和本地化问题感到沮丧。之前的帖子已经详细介绍了这些问题,因此我不会在这方面添加任何内容。
长话短说,最终我在 Visual C++ 标准 C 库上编写了自己的 UTF-8 兼容库层。基本上,这个库确保标准 C 程序在任何代码页中都能正常工作,内部使用 UTF-8。
该库称为 MsvcLibX,可在https://github.com/JFLarvoire/SysToolsLib上以开源方式获取。主要特点:
更多详细信息,请参阅GitHub 上的 MsvcLibX 自述文件,包括如何构建该库并在您自己的程序中使用它。
上述 GitHub 存储库中的发布部分提供了多个使用此 MsvcLibX 库的程序,这些程序将显示其功能。例如:尝试使用我的 which.exe 工具,在 PATH 中使用具有非 ASCII 名称的目录,搜索具有非 ASCII 名称的程序,并更改代码页。
另一个有用的工具是 conv.exe 程序。该程序可以轻松地将数据流从任何代码页转换为任何其他代码页。它默认是在Windows代码页中输入,并在当前控制台代码页中输出。这允许在命令控制台中正确查看 Windows GUI 应用程序(例如:记事本)生成的数据,使用如下简单命令:type WINFILE.txt | conv
这个 MsvcLibX 库并不完整,欢迎贡献改进它!
命令CHCP显示当前代码页.它有三个数字:8xx,与Windows 12xx不同.因此,键入一个只有英文的文本,您将看不到任何差异,但会错误地打印扩展代码页(如Cyrillic).
归档时间: |
|
查看次数: |
227997 次 |
最近记录: |