()为什么要编码
由于人类的语言有太多因而表示这些语言的符号太多无法用计算机中一个基本的存储单元byte 来表示因而必须要经过拆分或一些翻译工作才能让计算机能理解
所以总的来说编码的原因可以总结为
计算机中存储信息的最小单元是一个字节即 个 bit所以能表示的字符范围是 ~ 个
人类要表示的符号太多无法用一个字节来完全表示
要解决这个矛盾必须需要一个新的数据结构 char从 char 到 byte 必须编码
()编码方式
ASCII 码
ASCII 码总共有 个用一个字节的低 位表示~ 是控制字符如换行回车删除等~ 是打印字符可以通过键盘输入并且能够显示出来
ISO
个字符显然是不够用的于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码它们是 ISO~ISO其中 ISO 涵盖了大多数西欧语言字符所有应用的最广泛ISO 仍然是单字节编码它总共能表示 个字符
GB
它的全称是《信息交换用汉字编码字符集 基本集》它是双字节编码总的编码范围是 AF其中从 AA 是符号区总共包含 个符号从 BF 是汉字区包含 个汉字
GBK
全称叫《汉字内码扩展规范》是国家技术监督局为 windows 所制定的新的汉字内码规范它的出现是为了扩展 GB加入更多的汉字它的编码范围是 ~FEFE(去掉 XXF)总共有 个码位它能表示 个汉字它的编码是和 GB 兼容的也就是说用 GB 编码的汉字可以用 GBK 来解码并且不会有乱码
UTF
说到 UTF 必须要提到 Unicode(Universal Code 统一码)ISO 试图想创建一个全新的超语言字典世界上所有的语言都可以通过这本字典来相互翻译
UTF 具体定义了 Unicode 字符在计算机中存取方法UTF 用两个字节来表示 Unicode 转化格式这个是定长的表示方法不论什么字符都可以用两个字节表示两个字节是 个 bit
所以叫 UTFUTF 表示字符非常方便每两个字节表示一个字符这个在字符串操作时就大大简化了操作这也是 Java 以 UTF 作为内存的字符存储格式的一个很重要的原因
UTF
UTF 统一采用两个字节表示一个字符虽然在表示上非常简单方便但是也有其缺点有很大一部分字符用一个字节就可以表示的现在要两个字节表示存储空间放大了一倍
在现在的网络带宽还非常有限的今天这样会增大网络传输的流量而且也没必要而 UTF 采用了一种变长技术每个编码区域有不同的字码长度不同类型的字符可以是由 ~ 个字节组成
UTF 有以下编码规则
如果一个字节最高位(第 位)为 表示这是一个 ASCII 字符( F)可见所有 ASCII 编码已经是 UTF 了
如果一个字节以 开头连续的 的个数暗示这个字符的字节数例如xxxxx 代表它是双字节 UTF 字符的首字节
如果一个字节以 开始表示它不是首字节需要向前查找才能得到当前字符的首字节
[java]view plaincopy
/**
*byteArraytoHex
*@paramb
*@return
*/
publicstaticStringtoHex(byte[]b){
Stringresult=;
for(inti=;i<blength;i++){
Stringhex=IntegertoHexString(b[i]&xFF);
if(hexlength()==){//高四位置为
hex=+hex;
}
result=result++hextoUpperCase();
}
returnresult;
}
/**
*chartobytearray
*@paramc
*@return
*/
publicstaticbyte[]charToByte(charc){
byte[]b=newbyte[];
b[]=(byte)((c&xFF)>>);
b[]=(byte)(c&xFF);
returnb;
}
/**
*chararraytoHex
*@paramc
*/
publicstaticvoidcharToHex(char[]c){
for(charcc:c){
byte[]b=charToByte(cc);
Systemoutprint(toHex(b));
}
}
publicstaticvoidmain(String[]args){
Stringname=Iam君山;
charToHex(nametoCharArray());
Systemoutprintln();
try{
byte[]iso=namegetBytes(ISO);
Systemoutprintln(toHex(iso));
byte[]gb=namegetBytes(GB);
Systemoutprintln(toHex(gb));
byte[]gbk=namegetBytes(GBK);
Systemoutprintln(toHex(gbk));
byte[]utf=namegetBytes(UTF);
Systemoutprintln(toHex(utf));
byte[]utf=namegetBytes(UTF);
Systemoutprintln(toHex(utf));
}catch(UnsupportedEncodingExceptione){
eprintStackTrace();
}
}
输出结果
D B C
D F F
D BE FD C BD
D BE FD C BD
FE FF D B C
D E B E B B
字符串I am 君山用 ISO 编码下面是编码结果
个 char 字符经过 ISO 编码转变成 个 byte 数组中文君山被转化成值是 f 的 bytef 也就是?字符所以经常会出现中文变成?很可能就是错误的使用了 ISO 这个编码导致的
字符串I am 君山用 GB 编码下面是编码结果
GB编码GB 字符集有一个 char 到 byte 的码表不同的字符编码就是查这个码表找到与每个字符的对应的字节然后拼装成 byte 数组如果查到的码位值大于 oxff 则是双字节否则是单字节双字节高 位作为第一个字节低 位作为第二个字节
从结果可以看出前 个字符经过编码后仍然是 个字节而汉字被编码成双字节在第一节中介绍到 GB 只支持 个汉字所以并不是所有汉字都能够用 GB 编码
字符串I am 君山用 GBK 编码下面是编码结果
你可能已经发现上图与 GB 编码的结果是一样的没错 GBK 与 GB 编码结果是一样的由此可以得出 GBK 编码是兼容 GB 编码的它们的编码算法也是一样的不同的是它们的码表长度不一样GBK 包含的汉字字符更多所以只要是经过 GB 编码的汉字都可以用 GBK 进行解码反过来则不然
字符串I am 君山用 UTF 编码下面是编码结果
用 UTF 编码将 char 数组放大了一倍单字节范围内的字符在高位补 变成两个字节中文字符也变成两个字节从 UTF 编码规则来看仅仅将字符的高位和地位进行拆分变成两个字节特点是编码效率非常高规则很简单
字符串I am 君山用 UTF 编码下面是编码结果
UTF 虽然编码效率很高但是对单字节范围内字符也放大了一倍这无形也浪费了存储空间另外 UTF 采用顺序编码不能对单个字符的编码值进行校验如果中间的一个字符码值损坏后面的所有码值都将受影响而 UTF 这些问题都不存在UTF 对单字节范围内字符仍然用一个字节表示对汉字采用三个字节表示
常见问题分析
中文变成了看不懂的字符例如字符串淘!我喜欢!变成了ì ? £ ?? ò ??? £ ?
字符串在解码时所用的字符集与编码字符集不一致导致汉字变成了看不懂的乱码而且是一个汉字字符变成两个乱码字符
一个汉字变成一个问号 例如字符串淘!我喜欢!变成了??????
将中文和中文符号经过不支持中文的 ISO 编码后所有字符变成了?这是因为用 ISO 进行编解码时遇到不在码值范围内的字符时统一用 f 表示这也就是通常所说的黑洞所有 ISO 不认识的字符都变成了?