小心Windows7的UTF-8代码页
目录
第1章小心Windows7的UTF-8代码页
1.1 UTF-16与UTF-8相互转换
发现Windows7的UTF-8代码页有问题的根源就在于UTF-16与UTF-8的相互转换。
1.1.1 使用Windows API
使用Windows API,可以实现UTF-16编码与UTF-8编码的相互转换,如下面的代码:
| 
 char a1[128]; wchar_t w = 0; int n1 = 0; wchar_t w1[128]; int m1 = 0; n1 = WideCharToMultiByte(CP_UTF8,0,&w,1,a1,128,NULL,NULL); m1 = MultiByteToWideChar(CP_UTF8,0,a1,n1,w1,128);  | 
WideCharToMultiByte把UTF-16编码转换为UTF-8编码;
MultiByteToWideChar把UTF-8编码转换为UTF-16编码。
API函数WideCharToMultiByte和MultiByteToWideChar能够正常工作的前提是:Windows系统已经安装了UTF-8代码页。Windows XP、Windows 7默认已经安装了UTF-8代码页,因此不用太担心。Windows Mobile系统就有些麻烦了:有些Windows Mobile安装了UTF-8代码页,而有些没有。所以,为了程序在所有Mobile设备上正常运行,是不能使用WideCharToMultiByte和MultiByteToWideChar的。
1.1.2 自己编码
UTF-16与UTF-8的相互转换,不能使用WideCharToMultiByte和MultiByteToWideChar,怎么办?所幸的是:UTF-16与UTF-8的相互转换是有严格的转换关系的。本文不做这方面的论述,直接上VC++代码:
| 
 /*******************************************************************\ 将 UTF16 字符串转换为 UTF8 字符串 pUTF16 [in] UTF16 字符串首地址 nLen16 [in] UTF16 字符串字符数,小于 0 表示以 \0 结尾 pUTF8 [out] UTF8 字符串首地址,可以为 NULL 返回:转换的 UTF8 字符个数 \*******************************************************************/ long UTF16to8(const wchar_t*pUTF16,long nLen16,char*pUTF8) { long nLen8 = 0; if(pUTF16) { if(nLen16 < 0) { nLen16 = wcslen(pUTF16) + 1; } } else { nLen16 = 0; } if(nLen16 > 0) { long i = 0; wchar_t u = 0; for(i = 0;i < nLen16;++i) { u = pUTF16[i]; if(u <= 0x7F) { if(pUTF8) { *pUTF8++ = (char)u; } ++nLen8; } else if(u <= 0x7FF) { if(pUTF8) {//110xxxxx 10xxxxxx pUTF8[1] = (char)(u & 0x3F | 0x80); u >>= 6; pUTF8[0] = (char)(u & 0x1F | 0xC0); pUTF8 += 2; } nLen8 += 2; } else { if(pUTF8) {//1110xxxx 10xxxxxx 10xxxxxx pUTF8[2] = (char)(u & 0x3F | 0x80); u >>= 6; pUTF8[1] = (char)(u & 0x3F | 0x80); u >>= 6; pUTF8[0] = (char)(u & 0x0F | 0xE0); pUTF8 += 3; } nLen8 += 3; } } } return nLen8; } /*******************************************************************\ 将 UTF8 字符串转换为 UTF16 字符串 pUTF8 [in] UTF8 字符串首地址 nLen8 [in] UTF8 字符串字符数,小于 0 表示以 \0 结尾 pUTF16 [out] UTF16 字符串首地址,可以为 NULL 返回:转换的 UTF16 字符个数 \*******************************************************************/ long UTF8to16(const char*pUTF8,long nLen8,wchar_t*pUTF16) { long nLen16 = 0; if(pUTF8) { if(nLen8 < 0) { nLen8 = strlen(pUTF8) + 1; } } else { nLen8 = 0; } if(nLen8 > 0) { long i = 0; unsigned char u = 0; for(i = 0;i < nLen8;) { u = pUTF8[i]; if(u >= 0xE0) { if(pUTF16) { *pUTF16++ = (pUTF8[i] & 0x0f) << 12 | (pUTF8[i + 1] & 0x3f) << 6 | (pUTF8[i + 2] & 0x3f); } i += 3; } else if(u >= 0xC0) { if(pUTF16) { *pUTF16++ = (pUTF8[i] & 0x1f) << 6 | (pUTF8[i + 1] & 0x3f); } i += 2; } else { if(pUTF16) { *pUTF16++ = u; } ++i; } ++nLen16; } } return nLen16; }  | 
1.2 测试代码
自己编写的代码在使用前,那是要测试的。下面就是测试代码。其实就是与WideCharToMultiByte和MultiByteToWideChar进行比较:
| 
 void Test() { char a1[128]; char a2[128]; wchar_t w; int n1 = 0; int n2 = 0; wchar_t w1[128]; wchar_t w2[128]; int m1 = 0; int m2 = 0; for(long i = 0;i <= 0xFFFF;++i) { w = (wchar_t)i; n1 = WideCharToMultiByte(CP_UTF8,0,&w,1,a1,128,NULL,NULL); m1 = MultiByteToWideChar(CP_UTF8,0,a1,n1,w1,128); if(m1 != 1 && w1[0] != w) {//WideCharToMultiByte与MultiByteToWideChar相互转换有问题 TRACE(_T("WideCharToMultiByte,MultiByteToWideChar=%04X\n"),w); } n2 = UTF16to8(&w,1,a2); m2 = UTF8to16(a2,n2,w2); if(m2 != 1 && w2[0] != w) {//UTF16to8与UTF8to16相互转换有问题 TRACE(_T("UTF16to8,UTF8to16=%04X\n"),w); } if(n1 != n2 || memcmp(a1,a2,n1)) {//UTF16to8与WideCharToMultiByte有出入 TRACE(_T("WideCharToMultiByte,UTF16to8=%04X\n"),w); } } }  | 
1.3 测试结果
测试结果是:
1、在Windows XP下一切正常;
2、在Windows 7(64位旗舰版)下出问题了:当w在0xD800~0xD98A之间时,UTF16to8与WideCharToMultiByte的转换结果不一致!
笔者相信自己的代码,同时又有Windows XP的证明,最终得出的结论就是:Windows7的UTF-8代码页有问题!
还好,0xD800~0xD98A的395个字符并不是很常用,否则Windows7保存的UTF-8文件到了Windows XP或Linux下显示出乱码,问题就严重了。
解决方案:UTF-16与UTF-8编码的相互转换,还是自己编码实现吧。
小心Windows7的UTF-8代码页的更多相关文章
- CodePage------Encoding 类支持的编码以及与这些编码关联的代码页(CodePage)
		
Encoding 类 .NET Framework 4 表示字符编码. 继承层次结构 System.Object System.Text.Encoding System.Text.ASCII ...
 - 刨根究底字符编码之七——ANSI编码与代码页(Code Page)
		
ANSI编码与代码页(Code Page) 一.ANSI编码 1. 如前所述,在全世界所有国家和民族的文字符号统一编码的Unicode编码方案问世之前,各个国家.民族为了用计算机记录并显示自己的字符, ...
 - CMD代码页
		
不同字符编码在CMD模式下会出现乱码,需要使用 chcp 代码页 命令来更改代码页显示正常. UTF-8 65001 简体中文 936 437 美国 850 多语 ...
 - 解决“在多字节的目标代码页中,没有此Unicode字符可以映射到的字符”
		
今天在处理Google网站管理员中的500错误时发现这样一些URL: http://www.cnblogs.com/Garnai/tag/3D%3F%96%CA/ http://www.cnblogs ...
 - Windows代码页、区域
		
目录 第1章代码页 1 1 代码页 1 1.1 单字节字符集 1 1.2 双字节字符集 1 1.3 多字节字符集 1 1.4 ANSI代码页 2 2 枚举代码页 ...
 - 更改cmd代码页,修正语言显示
		
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 rem 英文 chcp 437 rem 日文 chcp 932 rem 简体中文 chcp 936 re ...
 - warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
		
bug来源: 一直在看sift然后就手贱的给 opencv源码做注释,如果在vs里面打开会一直相安无事,但是问题出在我用了notepad++. 这样就报了标题的错误. 因为notepad++会以uni ...
 - Visual Studio在页面按F7不能跳转至cs代码页的解决方法
		
检查页面Page设置内的CodeBehind属性,看是否与代码页的文件名相同,不同则改正,问题得以解决.
 - warning: C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
		
------问题-------------------- Qt项目使用 VC++ 编译器出现此错误. warning: C4819: 该文件包含不能在当前代码页(936)中表示的字符.请将该文件保存为 ...
 
随机推荐
- Educational Codeforces Round 16 C
			
Description Find an n × n matrix with different numbers from 1 to n2, so the sum in each row, column ...
 - 幸运数字(number)
			
幸运数字(number) Time Limit:1000ms Memory Limit:64MB [题目描述] LYK最近运气很差,例如在NOIP初赛中仅仅考了90分,刚刚卡进复赛,于是它决定使用 ...
 - java初始化笔记
			
构造器初始化static块与非static块区别:对象创建时都做块的初始化,非static块仅在实例创建时初始化,static块在类被第一次加载时初始化数组初始化注意点:1.数组定义时最好在定义的同时 ...
 - #查找列表中元素,移除每个元素的空格,并查找以 a或A开头 并且以 c 结尾的所有元素
			
#!/usr/bin/env python #查找列表中元素,移除每个元素的空格,并查找以 a或A开头 并且以 c 结尾的所有元素. # li = ["alec", &quo ...
 - 优化Linux生产服务器的经验之谈
			
[51CTO独家特稿]如何优化自己的Linux生产服务器?本文结合实际的工作经验,总结了优化Linux生产服务器的九大要点.如果有些方法您尚未采用,不妨一试. 一.时间同步 生产环境下的服务器对时间的 ...
 - Ecplise软件Devices看到两个相同设备问题
			
Ecplise软件Devices看到两个相同设备问题 在使用过程中,连接一台设备,在Ecplise软件的Devices界面下突然看到2个设备,如下图: 图1 解决方案:先 kill-server, 再 ...
 - 【Unity3D游戏开发】GameObject.Find()、Transform.Find查找隐藏对象 (十)
			
GameObjectFindTransformFind查找游戏对象 前置条件 相关API 1 GameObjectFind 2 TransformFind 3 其他查找 实际测试 即使隐藏root节点 ...
 - Perform Cut Copy Paste Operations Using Cut_Region Copy_Region Paste_Region Commands In Oracle Forms
			
You can do Select, Cut, Copy and Paste operations on text items in Oracle Forms using Select_All, Cu ...
 - vb6.0安装程序制作图解教程
			
如何制作vb安装程序,是在学习Vb6.0过程中比较常见的一个入门问题. 在此笔者介绍一个最简单的安装方法,就是用VB自带的打包程序进行打包,虽然比较普通,不过内部却有不少窍门,相信这一点知道的人可能不 ...
 - 读写分离提高 SQL Server 并发性能
			
以下内容均非原创,仅作学习.分享!! 在 一些大型的网站或者应用中,单台的SQL Server 服务器可能难以支撑非常大的访问压力.很多人在这时候,第一个想到的就是一个解决性能问题的利器——负载均衡. ...