Windows程序员必须知道的字符编码和字符集
- 字符编码 (Character encoding)
在存储和传递文本过程中,为了使得所有电脑都能够正确的识别出文本内容,需要有一个统一的规则。
2. 字符集 (Character Set) )
一般情况,一种编码方式对应一种字符集。如 ASCII,对应 ASCII 字符集。GBK 编码方式对应 GBK 字符集。但是也有一种编码方式,多种字符集的,Unicode 字符集有多种编码方式,如 utf-8,utf-16 等。
3. ASCII
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):使用 7 个 Bit 表示,共 128 个字符,刚好占用了一个字节中的后 7 位,共包括33 个控制字符和 95 个可显示字符。
. 4. ANSI
ANSI (一种字符编码,此处不是表示美国国家标准学会的意思):ANSI 是为了让计算机支持更多的语言,而在 ASCII 的*础上的一种扩展字符编码。在不同语言操作上,ANSI 都表示当前计算机默认的编码方式。如在简体 Windows 操作下,ANSI 编码代表 GBK 编码;在繁体中文操作下,ANSI 编码代表 Big5 编码;在日文 Windows 操作系统中,ANSI 编码代表 Shift_JIS 编码;在英文操作系统下,ANSI 就是 ASCII 编码。
. 5. MBCS
MBCS(Multi-Byte Character Set),早在 1980 年,中国就提出了使用 GB2312 编码方式来描述汉字。后来其他东亚国家也利用这种方式扩展 ASCII 编码字符集。台湾地区 5 大
企业推出的繁体 Big5 码,香港新加坡等后来也利用。日本韩国也相应推出了自己的编码方式。其实在这里,BIG5 既是编码方式,也是字符集。
. 6. GB2312(Guo Biao 2312) )
用双字节表示汉字,但是为了完全兼容 ASCII。汉字区“高字节”范围为 0xB0-0xF7,汉字区“低字节”范围 0xA1-0xFE,占用的码位 72*94=6768 个。
. 7. GBK(Guo Biao Kuozhan)
后来发现 GB2312 的字符依然不够用,尤其是像“*”(***)字打不出来。然后决定汉字区的低字节完全不兼容 ASCII,只要当前字符属于汉字码,那么其后的字符也属于汉字码。这样在兼容 GB2312 的*础上,再添加了近两万字和字符(兼容繁体、日本汉字、韩国汉字等)。GBK 同样也是一种编码方式和字符集两种意义合于一体。
. 8. GB18030
有两版 GB18030-2000 和 GB18030-2005,是 GBK 的扩展版,并且完全兼容 GBK。GB18030和 utf-8 类似,是动态的,既有单字节字体,也有双字节字节(BGK),也有三字节,四字节字符。Windows 默认支持的是 GBK,若要支持 GB18030,需要下载安装单独的支持包。
. 9. Big5 5
1984 年台湾五家公司联合创立,称大五码,英文 Big5。Big5 也是双字节编码方式,收录了一万多字符,但是没有包含中文简体。Big5 目前也被香港、新家坡等地区国家使用。Big5 保存的文本,在简体操作系统下显示乱码,但是有些编辑器会自动识别,然后将其按 GBK 繁体字显示,即可正常显示。如 Notepad++即有此功能。
10. 全角
GBK 用两个字节重新表示了一遍在 ASCII 中出现的字符,这些字符被叫全角字符。
11. 半角
出现在 ASCII 中的字符,被称作半角字符。
12. e Unicode 字符集
Unicode 字符集是 1990 年提出的,能够表示全世界所有国家的所有字符。但是支持Unicode 字符集的编码方式都有多种,如 UTF-8,UTF-16 等。编码方式虽然不同,但是
都是可以完整表示所有 Unicode 字符的。VS 里默认的 Unicode 字符集是指 utf-16 编码的,即固定双字节为一个字符。
13. UTF- -8 8
因为 Unicode 能够包含全球所有的字符,而 utf-8 又是其中比较节省字符存储的一种实现方式。所以现在的网站,*本都是以 utf-8 编码方式来存储、传输和显示了。UTF-8
是变字节长度的,用 1-6 个字节表示字符。用一个字符表示常见字符,用二个字节表示拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚等,用三字
符表示常见汉字等,四个字节的字符比较少见。UTF-8 既能很方便存储英文,又能兼容全世界的字符,所以非常流行。
14. UTF- -8 8 解析算法
如果字节的第一位为 0,则 B 为 ASCII 码,并且 Byte 独立的表示一个字符;
如果字节的第一位为 1,第二位为 0,则 Byte 为一个非 ASCII 字符(该字符由多个字节表示)中的一个字节,并且不为字符的第一个字节编码;
如果字节的前两位为 1,第三位为 0,则 Byte 为一个非 ASCII 字符(该字符由多个字节
表示)中的第一个字节,并且该字符由两个字节表示;
如果字节的前三位为 1,第四位为 0,则 Byte 为一个非 ASCII 字符(该字符由多个字节表示)中的第一个字节,并且该字符由三个字节表示;
如果字节的前四位为 1,第五位为 0,则 Byte 为一个非 ASCII 字符(该字符由多个字节
表示)中的第一个字节,并且该字符由四个字节表示代码页(CodePag):代码页是字符集的数字值,不同的语言使用不同的代码页。例 如,ANSI
代码页为 1252,日文代码页为 932,简体中文(GBK)代码页为 936,繁体中文(Big5)代码页为 950。
15. 语言包
系统默认的是 ANSI,但是同时也兼容 Unicode,这也是 Windows 的 API 有两套。当系统识别到当前字符是 Unicode 时,会以对应的 Unicode 编码方式去解码,Windows 支持的是 utf-8。解码之后,需要正确地显示出相应的字形,这个时候就需要语言包。如果没有语言包,会出现乱码。英文操作下,东亚语言归为一类,需要单独安装,安装时提示230MB。因为语言包比较大,所以在英文操作系统下,默认没有安装东亚语言包的,即不能正确显示 Unicode 下的东亚文字。至于语言包为什么这么大,猜测语言包必须得描述这个字体是如何点钩撇捺的等等。Windows 可以在控制面板中设置非 Unicode 程序显示语言。
16. 大小端模式
由于 CPU 的缘故,存取数据分大端模式和小端模式,然后一些操作系统了软件也会有这样的区分。大端模式(Big-endian),是指数据的高字节,保存在内存的低地址中,而
数据的低字节,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放。小端模式(Little-endian),
是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分
权值低,和我们的逻辑方法一致。
17. BOM(Byte Order Mark, 字节顺序标记) )
有一些文本编辑工具支持 UTF-8(BOM),字符 U+FEFF 如果出现在字节流的开头,则用来标识该字节流的字节序,是高位在前还是低位在前。接收者收到 FEFF,就表明这个字节
流是 Big-Endian 的;如果收到 FFFE,就表明这个字节流是 Little- Endian 的。Windows 文本默认的 Unicode 为大端 utf-16 存储,即以 FFFE 开头。BOM 主要应用Windows 下,而在某些系统下可能会出错,如 PHP 中容易读错。
编码 | 以此标记 (十六进制) | 以此标记 (十进制) |
UTF-8 | EF BB BF | 239 187 191 |
UTF-16(大端序) | FE FF | 254 255 |
UTF-16(小端序) | FF FE | 255 254 |
UTF-32(大端序) | 00 00 FE FF | 0 0 254 255 |
UTF-32(小端序) | FF FE 00 00 | 255 254 0 0 |
18. GBK 、e Unicode 他 他 UTF- -8 8 转换
UTF-8 和 GBK 同属于多字节 MultiByte,Unicode 默认是 UTF-16,即宽字符,两个字节。
GBK(ANSI)->Unicode->UTF-8,反向即 UTF-8->Unicode->GBK(ANSI)。
-
- // 注释:多字节包括GBK和UTF-8
- int GBK2UTF8(char *szGbk,char *szUtf8,int Len)
- {
- // 先将多字节GBK(CP_ACP或ANSI)转换成宽字符UTF-16
- // 得到转换后,所需要的内存字符数
- int n = MultiByteToWideChar(CP_ACP,0,szGbk,-1,NULL,0);
- // 字符数乘以 sizeof(WCHAR) 得到字节数
- WCHAR *str1 = new WCHAR[sizeof(WCHAR) * n];
- // 转换
- MultiByteToWideChar(CP_ACP, // MultiByte的代码页Code Page
- 0, //附加标志,与音标有关
- szGbk, // 输入的GBK字符串
- -1, // 输入字符串长度,-1表示由函数内部计算
- str1, // 输出
- n // 输出所需分配的内存
- );
- // 再将宽字符(UTF-16)转换多字节(UTF-8)
- n = WideCharToMultiByte(CP_UTF8, 0, str1, -1, NULL, 0, NULL, NULL);
- if (n > Len)
- {
- delete[]str1;
- return -1;
- }
- WideCharToMultiByte(CP_UTF8, 0, str1, -1, szUtf8, n, NULL, NULL);
- delete[]str1;
- str1 = NULL;
- return 0;
- }
- //UTF-8 GBK
- int UTF82GBK(char *szUtf8,char *szGbk,int Len)
- {
- int n = MultiByteToWideChar(CP_UTF8, 0, szUtf8, -1, NULL, 0);
- WCHAR * wszGBK = new WCHAR[sizeof(WCHAR) * n];
- memset(wszGBK, 0, sizeof(WCHAR) * n);
- MultiByteToWideChar(CP_UTF8, 0,szUtf8,-1, wszGBK, n);
- n = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
- if (n > Len)
- {
- delete[]wszGBK;
- return -1;
- }
- WideCharToMultiByte(CP_ACP,0, wszGBK, -1, szGbk, n, NULL, NULL);
- delete[]wszGBK;
- wszGBK = NULL;
- return 0;
- }
19. 附ASCII码图表