详谈字符编码[二]代码页和一个乱码案例

        上一篇关于字符编码的随笔介绍了编码,输入码,机内码,字形码,字形库等概念。除此以外,还有一些其余的概念咱们不得不了解,它们已经不属于如今,可是却时常影响着如今。代码页,正是这些有历史感的概念之一。这篇博文带你了解代码页和当前Windows对Unicode和ANSI编码的支持状况,末尾分析了一个乱码的案例,出于某知名软件,你必定不想错过。windows

Windows的默认编码?

        偶尔在知乎看到这样的问题:为何中文Windows选择GBK做为默认编码?其实会有这样的误解也难怪,为何这么说呢?你们都从控制台的Helloworld开始,后来想要输出中文时天然先想到printf("你好,世界");运行发现真的出现了中文,仿佛英文和中文没什么区别,世界很美好的感受。但学习更多以后发现Windows下的strlen("你好,世界")的值居然是10(严格说是MinGW下使用GBK做源编码时或者使用VS时才是10),第一次感受到了英文字母与汉字的区别,因而咱们去寻找缘由,终于得知GBK编码之类的各类编码,也知道了代码页这个使人疑惑的名词。你好世界,世界倒是灰色的。学习

        实际上微软早就声明:“UTF-16Little-endian是Microsoft以及Windows操做系统中的编码标准”。在Windows2000之前的操做系统上,内码的编码是和语言相关的(ANSI编码)。那时候简体中文版的Windows使用GBK,全部的中文的软件中的字符串也都是GBK,因此在window上运行也不会乱码。可是可想而至,这两者一旦不匹配就会出现乱码。不一样语言国家的Windows编码都不同,所以微软使用了代码页来解释字符编码,好比简体中文版的Windows默认代码页就是GBK,这意味着默认使用GBK来解释字符串,因此能显示中文是必然的,显示其余的语言(好比日语)是乱码也是必然的。字体

        Windows2000以后(严格说是Windows NT 3.1以后)默认使用UTF-16做为编码标准。这是什么意思呢,意思就是全世界的字符你均可以处理,若是安装了相应的字体,你还能够显示所有字符,若是安装了相关输入法你还能够输入任意一种语言。可是哪些不是UTF-16编码的程序还能在新平台下运行吗?能够的,在这一方面微软仍是负责任的,毕竟当初是本身提出的代码页方案,不能把软件开发商们都得罪了。因此直到今天(Windows 10)微软都是兼容两者,可是提倡使用UTF-16。那么Windows的默认编码是什么呢?事实是最好不要使用”默认编码“这个词,由于根本没有什么默认的编码(你能够决定使用任何一种编码,只不过别人不认识而已),推荐使用官方的说法“编码标准”,并且微软的编码标准是UTF-16L。编码

  之因此不少初学者有误解,是由于一开始的程序基本都是控制台程序,而控制台的默认代码页确实是GBK。使用chcp命令能够查看当前代码页,能够看到回显Active code page: 936,这正是表明GBK。可使用命令“chcp 65001”切换到UTF-8。控制台为了兼容性默认代码页是936,不表明Windows的编码标准是GBK,下面的试验都在对话框上显示,由于这是最简单的检验GUI编码方式的方法。spa

Windows对两种机制的兼容

那么具体Windows是怎样同时兼容两者:既支持UTF-16,又可使用ANSI编码的呢?使用一个MessageBox作一下试验。操作系统

1 #include<windows.h>
2 int main() {
3     MessageBox(NULL, L"你好,世界", L"你好,世界", 0);
4     return 0;
5 }

效果是下面图1这样翻译

     

           图1                                             图2代理

         图3日志

        但你们都知道,这里是使用了(如图2),调用MessageBox()其实是调用了MessageBoxW()MessageBoxW()的参数是wchar_t类型的,wchar_t*的字符串字面量通常被实现成UTF-16编码。与之对应的是MessageBoxA()MessageBoxA()接受的参数是char*类型的,char*的字符串字面量被实现成ANSI编码。我说的字符串字面量被实现成某某编码是什么意思呢?用图片解释一下(图3),两个字符串虽然都是“你好,世界”可是运行时的样子彻底不一样。要强调机器只认识二进制,因此对机器来讲这俩个字符串没有任何相同点。咱们若是就是调用MassageBoxA(),传入char*会显示什么呢。code

#include<windows.h>
int main() {
    MessageBoxA(NULL, "你好,世界", "你好,世界", 0);
    return 0;
}

       很意外,结果居然和图1彻底同样。两个彻底不一样的二进制串居然显示了相同的正确结果。其实这就是所谓的Windows兼容两种编码(UTF-16与ANSI编码),虽然推荐使用UTF-16可是,使用GBK也能正常在Windows的GUI中显示,可是你的应用程序已经被Windows归类为“非Unicode程序”。这里你们能够打开控制面板--时钟语言和区域--区域--管理,能够找到一个设置项:非Unicode程序的语言。能够选择中文或者其余语言,那它有什么影响呢?你不妨尝试一下改为日语或者韩语什么的,再运行第二段代码,你就能看到乱码了。可是除此以外几乎感觉不到影响,程序们仍是正常的运行着,没有出现乱码,这说明如今的绝大多数程序都是使用的UTF-16编码,因此这一个设置对他们根本没有影响。

        但也并不绝对,在笔者把这一项设置修改成“日语(日本)”一周后(我已经忘了本身没有改回来,由于确实没有什么影响)看到一个奇怪的文件夹,他的名字是:ムクタラマツヤリ。是哪个程序搞出了这样的乱码呢?文件夹名字原来是什么呢(不要期望这是日语,这只是日语字符组成的乱码,不能看出含义)?下一节咱们来分析这个例子,揭开这个还在使用ANSI编码的程序的羞耻的面纱。

一个乱码的例子分析

        日本的ANSI编码是Shift_JIS,它是在有Unicode以前日本国内计算机的编码方式。可想而知,之因此出现ムクタラマツヤリ这一段乱码,就是由于我把非Unicode程序的语言设定成了日语(日本),因此致使某个想要用GBK字符串命名文件夹的程序建立了乱码的名称。如今可以查到这些字符的Unicode编码(复制粘贴后,他已经变成了Unicode),因此把Unicode转换成的Shift_JIS二进制串解释为GBK就获得文件夹原本的名字了。

ff fe 91 ff 78 ff 80 ff 97 ff 8f ff 82 ff 94 ff 98 ff<--这是UTF16小端编码,开头的0xfffe是BOM,不知道BOM是什么的能够查看详解字符编码[一]

d1 b8 c0 d7 cf c2 d4 d8<--这是对应的Shift_JIS

把上面的二进制翻译成GBK就是答案:迅雷下载

我用的正是最新版本的迅雷:9.1.41.914。迅雷的一个小Bug就这样被我发现了。

日志:在把非Unicode程序的语言改成日语后,只有一个MFC的上古程序和最新版的迅雷出现了乱码,2017年10月9日。

 下一篇会介绍C/C++程序避免乱码的方法并介绍怎样在Java中处理UTF-16的代理对。喜欢请给个推荐,再见。

相关文章
相关标签/搜索