1.首先总结几个概念:
- Unicode是全球最全的字符格式。iconv 源代码里面所有转化都是先将原格式转成”UCS-4”(用四个字节来表达UNICODE的格式),然后在转成目标格式。 绝大多数字符的UNICODE码都是2字节的,UCS-2就够了,iconv为了作为中间格式选择为4字节,不足4字节的高位都是0。下面是iconv的定义代码:
/* Our own notion of wide character, as UCS-4, according to ISO-10646-1. */
typedef unsigned int ucs4_t;
UTF-8 是Unicode的一种存储实现方式,编码就是UNICODE码。因为 UCS-4比较浪费空间。比如:UTF-8就是把一个Unicode编码按照规则把bit位 拆开后,存到变长的mb 字符串中具体原则也非常简单。详细规则就不说了,很容易搜到。 通常汉字的UTF-8是三个字节。所以可以说 UTF-8跟UCS 没有本质区别。UTF-16在表达两个字节的UNICODE是就跟UCS-2是相同的。
GB2312,GBK, GB18030都是中文字符集,按时间发展来的。
按照集合的大小来分类,可以简单粗暴地概括为:GB2312 < GBK < GB18030
GBK经过几次扩容,但是很多字符还是没地方放了,于是就放在PUA(用户使用)里。- CP936是微软定义的字符格式,可以认为跟GBK是相同的,当然网上说有几个字符是有细微差别的,通常基本上可以忽略,而且Iconv里面CP936转其他的时候会先尝试用GBK,失败了再去用CP936. 可以认为CP936是GBK的最新扩展集合的扩展集合。但是跟GB18030 还是不一样的。
- 可以说在windows的系统里 GBK = CP936 = ANSI(系统会自动判断英文存ANSIC,中文存CP936)。这个用UE也可以看到选项就是:
936 (ANSI/OEM -简体中文 GBK)
下面说我的问题:
- 经过测试:
繁体 “鶄” iconv(“GBK”, “UCS-2BE”) , iconv(“GBK”, “UNICODEBIG”) 结果都是对的 849D (左边是高地址,注意Utrl—Edit显示是左边低地址)
简体 “” iconv(“GBK”, “UCS-2BE”) ,iconv(“CP936”, “UCS-2BE”) 都失败。
原因是:
GBK增补的80个字符本来是放在Unicode PUA区的,后来又被新版 Unicode收录。所以既可以用PUA区的编码表示,也可以用非PUA编码表示。 具体参考:
http://www.fmddlmyy.cn/text24.html
- 实际上:简体 “” 是有两个Unicode编码的 PUA: E85F 非PUA:4D16.
问题根本原因是:window和linux对这个字的所采用Unicode解析方式不同。 - 在window记事本另存为UTF-16的时候就是E85F,换言之window用CP936来处理这个字符,把这个字符当成GBK的一种,直接转到 Unicode的 PUA来显示。估计微软当年遇到了这个问题,他不想废弃CP936,就对这些些字符进行了定制化处理。
- 而在Linux上 的字符处理是依赖于iconv。iconv认为该字符不是GBK也不是GBK扩展,而是GB18030. linux对 PUA码也不做处理,已经挪为他用。
- 这点可以通过 在linux系统用文本文件存储”” 字,然后查看的utf-8 编码跟windos下 不一样来证明。
- windows保存的UTF8格式的TXT放在linux下面显示成为 “草字头下面加然字”。linux下的UTF8编码是: E4B496 对应 Unicode为 “4D16” 这个新版Unicode的编码。
【最终结论】 用GB18030 替代 GBK在incov中使用,不再使用GBK。
如: iconv(“GB18030”, “UTF-8”)