在编程过程中,字符的编码问题不可避免。以Web应用为例,HTTP协议中报文头里的Accept-Charset、Accept-Encoding、Content-Encoding等就是字符编码的一种体现。当我们充分理解字符编码的原理后,我们就可以避免在看到诸如??OY}T??N*NuL?
乱码的情况。
基本概念
编码:把一种信息格式转换为另一种信息格式。对与计算机而言,此过程是把我们自然世界里的字符、声频、音频等转换为二进制形式。
解码:编码的逆过程。
乱码:不能正确显示字符的现象,原因多是编码与解码所用的映射关系不匹配。
字符集:某一系统所有字符的集合。如:中文字符集包括汉字、汉字符号等。
字符编码:把字符集中的字符编码成指定集合中的对象。如Unicode编码、GB2312字符编码。通常,人们认为字符集和字符编码是同义词(因为特定的字符编码会有唯一的字符集与之对应)。
各国编码
了解了编码的基本概念之后,我们需要明确一下,各国是如何对本国字符进行编码的:
ASCII编码
由于计算机是西方国家产物。因此,在计算机被发明的很长时间内,都是使用一种叫ASCII码的东西对西方国家常用的字符进行编码。
ASCII使用一个字节存储字符,且第一位同一为0,实际只能表示128个字符。对应的码表可以在这个网站查看。
各国独立编码
虽然ASCII对于美国等西方国家使用时勉强足够,但是对非英语系的国家而言,其字符远远不知128个,因此,各国纷纷拟定能够容纳本国字符的编码。比方中国的GB2312编码、日本的ISO2022编码。
Unicode编码
各国使用各自的编码,在本国使用当然不会有问题。如果编码的对象需要在互联网上传输、共享,还是会有乱码的发生。因此,一个怀着「把世界上所有的字符都用同一编码」伟大梦想的,统一码联盟诞生了。这个联盟拟定的编码方式如:UTF-32(使用4个字节存储一个字符);UTF-16(使用2个字节存储一个字符);UTF-8(使用1-4个字节变长编码)等实现。
UTF-8编码
我们知道UTF-8编码只是Unicode编码的一种实现,但在编程中,UTF-8也是最经常使用的。因此,这里简单介绍一下UTF-8的编码策略。
对于UTF-8编码的字符,第一个字节里的高x位1表示该字符用x位字节编码;接下来的x位前两位必是10。下面的表格简单模拟了UTF-8编码过程:
码点位数 | 码点起值 | 码点终值 | 字节序列 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 |
---|---|---|---|---|---|---|---|---|---|
7 | U+0000 | U+007F | 1 | 0xxxxxxx | NULL | NULL | NULL | NULL | NULL |
11 | U+0080 | U+07FF | 2 | 110xxxxx | 10xxxxxx | NULL | NULL | NULL | NULL |
16 | U+0080 | U+FFFF | 3 | 1110xxxx | 10xxxxxx | 10xxxxxx | NULL | NULL | NULL |
21 | U+10000 | U+1FFFFF | 4 | 11110xxx | 10xxxxx | 10xxxxxx | 10xxxxxx | NULL | NULL |
26 | U+200000 | U+3FFFFFF | 5 | 111110xx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | NULL |
31 | U+4000000 | U+7FFFFFFF | 6 | 1111110x | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
由上面的表格可以看出,当高1位为0时,UTF-8编码和ASCII编码一致。
编码应用
在JEE开发过程中,假定有如下JSP页面:
<%@ page pageEncoding="UTF-8" contentType="text/html;charset=UTF-8"%>
<%
out.print("中国");
%>
在这里:
- pageEncoding:JSP转换成Servlet时使用的编码
- contentType:获取printWriter时,使用的编码
两者使用的映射一样,不会出现乱码。