剖析字符集以及编码

 

本人是本人根据自己分享的PPT写的文章

很多编程的朋友弄不清字符集编码以及编码字符集,往往觉得转来转去,出现了乱码,却无能为力,大部分的原因是来源于不清晰的历史,本文的作用就是为各位朋友从本质上去了解他们。本文分为两部分,一部分是编码字符集与字符集,一部分是JAVA的编码。

第一部分:编码字符集与字符集

1)   编码字符集与字符集编码

抽象编码字符集:一种抽象字符的集合,抽象意味着与具体形式无关,是一种意识形态,例:“中”无论表现为声音、拼音、图片、宋体,点矩阵,矢量……都是抽象的“中”字的具体表现形式。抽象意味着,某个字符只是存在人的脑子里面,是一种意识形态,把一组即一个集合的抽象字符集中在一起即是抽象编码字符集,这个集合是划分是由人来定义的,你也可以使用26个英文字母来作为一个集合,也可以使用简体的汉子来作为一个集合。

编码字符集:抽象编码字符集的每个字符都分配了一个整数编号以后(连贯?),这个字符集就有了顺序,就成了编码字符集,同一个抽象的字符在不同的编码字符集中的所分配的整数编号不一定相同。例:“儿”在unicode的编号为0x513F,在Big5是0xA449

字符集编码: 是对前面说的编码字符集进行编码,决定了如何将一个字符的整数编号对应到一个二进制的整数值,是一种整数到整数的函数映射关系

三种名词的关系:抽象编码字符集,只是存在人脑的意思中,他的抽象个体只有一个,具体表现形式可以有很多个,编码字符集呢,也叫字符集,是计算机为了表示抽象编码字符集的一种方案,定义了一序列的整数来对应,字符集编码,也叫编码,是为了存储、传输、等的一种编码方案,真正被计算机使用到的就是字符集编码。

有哪些编码字符集: Unicode UCS GB2312GBK BIG5 ISO-8859-1

UTF-8,UTF-16呢? 这两个都不是,这两个就是字符集编码,是unicode的字符编码,当然也有编码字符集名称跟字符集编码名称一样的叫法,比如字符集为GB2312的,其编码也叫GB2312,类似的GBK,ISO-8859-1,等

字符集编码常见方式:

•        整数到整数直接映射,比如20à20(简单)

•        按照一定的规则进行转换(可能省空间)

•        部分字符直接映射,部分按一定的规则转换

编码字符集跟字符集编码的对应关系

 

2)   编码字符集的简史

早期阶段:ASCII 、ISO系列,支持英文,希腊等以字母的语言,各自国家进行自己编码:比如中国,GBK,中国台湾big,日本的ISO-222-JP,JIS,越南等具有汉字根源的.天下大乱了,需要大统一主要是UNICODE,UCS,Unicode 与UCS 合并在一起(1991年开始)

合并阶段:合并阶段主要UNICODE与UCS的合并,早期Unicode:16位,共65536位,UCS:32位,共65536 * 65536 = 40个亿 (恐怖),合并成现在的UNICODE: 1112064(不再是65536),这个算法如图:

•     增补字符的表示:2048个位置来做特殊处理。正常的UNICODE则占两个字节,增补的则四个字节,65536 – 2048 + 1024 * 1024 = 1112064 (1024 * 1024 是一种组合方式),这种方式的坏处是破坏了JAVA中的char,JAVA中的char是一个16位的UTF-16编码的,也意味着,一个增补的字符需要用2个char表示,破坏了String(char数组)的长度定义。

•     BMP:就是指单纯的65536 – 2048个字符

•     UNICODE使用情况:截止UNICODE 5.0,大概使用了25万,其中汉字编码使用了7万多,(目前已知的汉字大概是9万多)

3)   主要的编码规则

•         ISO-8859-1(ISO-8859-1的编码):单字节,支持英文字母跟英文符号,扩展ASCII,并兼容,好处?范围:从00到FF ,如果我中文“汉”用ISO-8859-1来编码会如何?,因为中文“汉”字的编码范围超出了ISO-9959-1的,也就是说ISO-8849-1在它的范围内无法为“汉”字编码,那么它采取的策略就是把该字符编码为“?”号,表示不认识,即63(十进制的63)

•         GB2312(GBK2312的编码):变长,英文为单字节,中文为双字节支持英文字母跟英文符号,扩展ASCII,并兼容,(兼容为了省空间),范围:不全满(有些码位是无意义的,会导致从别家的编码转换为GB2312的时候,该好落在无意义的码,那么他会采取像ISO-8859-1的策略,定义为一个“?”号,会丢失编码信息),注意:高字节第1位都是1。原因是为了兼容ASCII,能力:能包括6763汉字

•         GBK(GBK的编码):与GB2312基本一样,兼容GB2312,增加了一些辟生字以及繁体,范围:依然不全占满码位,能力:21886

•         GB18030(GBK18030的编码):与GB2312基本一样,兼容GB2312,增加了一些辟生字以及繁体,范围:依然不全占满码位,能力:27484,注意:支持单字节,双字节,四字节

•         UTF-8(UNICODE的编码):

<80 1字节 0xxxxxxx

说明:110万,用21位即可,也就是最多用到4个字节(x才是真正的位,其他的是标识位),那为什么还有5字节,6字节的呢,这是UTF-8为了兼容UCS的编码。

•       BOM:UTF-8  有字节序(BOM byte order mark)问题吗?(字节序就是0X1122在传输的过程中11在前还是22在前,答案是没有字节序的问题), UTF-8 有个EF BB BF 头标识符,作用不是字节序,是为了标识该文件或者该字节流是用UTF-8来编码的,所以就导致了有些UTF-8编码的文件有些有EF BB BF,有些没有。源码为FFFE(可选,引起了很多问题eclipse认得,但是javac认不得,php都不认)

•         UTF-16(UNICODE的编码):UTF-16 与Unicode 一一对应,当然UTF-16支持增补,这是导致UTF-16 被很多人认为是一种编码字符集的原因,再次重申UTF-16是编码,而不是字符集,UTF-16 有BOM (FFEF)问题吗?为什么?答案:是有的,因为没有特殊的标识位,不像UTF-8一样。

•         UTF-32(UNICODE的编码):使用了10FFFF个码,都是使用4个字节来表示字符,有BOM问题

•         UCS-2(UCS的编码):与UTF-16 相似,不同是不能表示增补字符

•         UCS-4(UCS的编码):与UTF-32 一样,所有都是用四个字节表示

 

第二部分:JAVA的编码

1)    JAVA与编码:JAVA是在1991年发明的,JAVA前身更早点,那意味着JAVA得面对头痛的编码字符集,如果你是一个JVM的设计者,你会如何设计?早期,JAVA使用了早期的UNICODE,也就是65536个码位,现在JAVA使用了合并后的UNICODE,也就是110多万的码位,JAVA在JVM内存中的表现形式位UTF-16(BE)形式。很多人也叫UNICODE,其实这种叫法不太准确,因为UNICODE是一种字符集,而不是编码

•    char: 就是早期的UTF-16编码,对应UNICODE的整数Char只能表示BMP范围的,对于增补字符,是无法用char表示的,需要用Character的封装类

•    String :就是char的数组,大家猜猜String的长度是字符的长度还是char的长度,一个增补字符会是多长?答案是2,因为一个增补要2个char来表示,

•    代码点(CodePoint): 就是UNICODE的整数序号,这个是每个字符都不变的

•    代码单元(CodeUnit):跟编码有关,即该编码最少能表示一个字符的位数:ISO-8859-1 为8位,UTF-8为8位,UTF-16为16位,UTF-32为32位

•    增补的表示方法:

2)   JAVA运行程序的整个流程

•    1 你写的JAVA类以编码A保存在系统

•    2 调用JAVAC来编译你的类,你要指定编码方式JAVAC才能读取你的类,然后内存转换,编译

•    3 JAVAC编译后存入系统,以UNICODE的存Classs

•    4 JAVA 运行,则读取Class,调用系统本地的读文件方法,读入内存,然后直接以UTF-16(BE)放到JVM内存中(不需要转码)

•      内存转换:JAVA自动调用了 在JVM跟系统的边界处自动调用了转换类,边界处:跟输入输出有关,包括Console窗口,system.out.println等

•      Eclipse设置:这个设置了1,2步

•      设置不对时:如果你设置为ISO-8859-1的话,你根本不能保存中文,如果你设置为A编码,但是你在Eclipse外去编译,则可能编译不过,因为java编译的时候会读取系统文件file.encoding,中文是GBK,而Eclipse的设置正是在Elicpse范围内改变该文件

 

3)   JAVA的乱码

•      WEB文件的设置 :JSP文件中的Charset 或者Response的ContentType, 其实不是指编码字符集,而是指字符集编码(脑残吧)JSP文件的pageEncoding,其实是给程序使用的,比如Tomcat,比如你用GBK保存JSP文件,而你在JSP的头标签pageEncoding=UTF-8,就可能出现乱码。(如何读到pageEncoding ?)Tomcat以contentType="###,charset='',在XML的Encoding 这回更正回来,是字符集编码,Tomcat以contentType=“###,charset=‘’来告诉浏览器,底层用ISO-8859-1编码通信,在XML的Encoding 这回更正回来,是字符集编码,静态的HTML有个标签中也指定了charset。静态文件请求头没有HTPP头(没有经过我的验证,参考别人的博客,有问题请提出?),浏览器会根据这个来识别

•      一种乱码解决方法的原理:你是否曾经使用这样去解决乱码:

new String (b.getBytes("iso-8859-1"),"utf-8"));

全部应该是这样的:

模拟网络的情况

                String a = "中国";

    byte [] bytes = a.getBytes("utf-8");

    String b = new String (bytes,"iso-8859-1");   //网络传输时自动的一步,应该是tomcat自动转换的一步

   

    System.out.println(new String (b.getBytes("iso-8859-1"),"utf-8"));

•         神奇的ISO-8859-1:神奇之处:在它范围内的所有码都用完,单字节,有用之地:很多底层的传输都是用了它作为编码,HTTP,有些数据库。无论你用什么方式编码,只要封装一层它,都不会丢失信息,因为它是有效编码占满了255.

•         GBK、GB2312的可惜之处:

一个“汉”字UTF-8是3个字节,  

   String a = "汉";

 byte [] bytes = a.getBytes("utf-8");

 String b = new String (bytes,"gbk");

Gbk只能解析为一个汉字,最后一个字节不认识了(不是0开头的),那它就会默认转换成一个“?”的GBK字符,这就丢失了 ,类似的还有

UTF-8也会这样,UTF-8的每个字符的高位都有限制的

0000 - 007F    0xxxxxxx

0080 - 07FF    110xxxxx 10xxxxxx          

0800 - FFFF    1110xxxx 10xxxxxx 10xxxxxx

比如你来了一个 10开头的字节,UTF-8就傻了

UTF-16 :由于有了2408位是无对应码(无码,嘿嘿),也会导致无法解析问题

UTF-32: 由于10FFFF之后就无码了,还是导致无法解析

  总结:utf-16与utf32可以逆转换(去掉utf-16的bom,或者加上bom)对于兼容的编码,可以向上转换,比如GB2312可以大胆得转为GBK,而不会丢失编码信息,所谓的字符跟字节,其实系统底层都只是0跟1 ,只是不同的叫法而已(人能看懂的文件就叫字符),本质上都是字节。

 

 本篇内容很多,对于新手不可能一下子就能看得懂的,鄙人也是经过了挺长时间的摸索,测试,才得出的结论,才能得出事情的本质。由于鄙人的能力有限,上诉内容存在不对的地方,望能指出一起讨论。

下面是学习的过程的一些其他网友的链接。

http://www.iteye.com/topic/710998

http://jiangzhengjun.iteye.com/blog/512083

http://www.blogjava.net/javavle/archive/2011/02/15/344364.html

http://topic.csdn.net/t/20060828/17/4981576.html

搜索引擎 网页编码

http://xiangxingchina.iteye.com/blog/795200

 

本人联系方式:[email protected]  [email protected]

              Qq:562814993

上班期间qq邮箱是上不了!

你可能感兴趣的:(编程基本知识:编码,java,string,eclipse,tomcat,jsp,encoding)