Java Web中的中文编码

1、为什么需要编码

        不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题,必须要回答计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。由于人类的语言太多了,表示这些语言的符号太多了,无法用计算机中的一个基本存储单元——字节(byte)来表示,因而必须要经过拆分或一些翻译工作,才能让计算机理解我们的语言。

        我们可以把计算机能够理解的语言假定为英语,其他语法要能够在计算机中使用,必须得经过一次翻译,把它翻译成英语。这个翻译得过程就是编码。所以可以想象,只要不是说英语得国家,要使用计算机就必须经过编码。这看起来有些霸道,但这就是现状。所以总结起来,编码的原因有以下几条:

  • 在计算机中存储信息的最小单元是1个字节,即8个bit,所以能表示的字符范围是0~255个。
  • 人类要表示的符号太多,无法用1个字节来完全表示。

要解决这个矛盾必须要有一个新的数据结构char,而从char导byte必须编码。

2、如何翻译

        计算机中提供了很多种翻译方式,常见的有ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16等,它们都可以被看作字典,他们规定了转化的规则,按照这个规则就可以让计算机正确地表示我们的字符。目前的编码格式很多,如GB2312、GBK、UTF-8、UTF-16都可以表示汉字,那我们到底选择那种编码格式来存储汉字呢?这就要考虑其它因素了。例如,是存储空间重要还是编码效率重要,下面简单的介绍下这几种编码格式。

1、ASCII码

         学过计算机的人都知道ASCII码,总共有128个,用1个字节的低7位表示,0~31是控制字符如换行、回车、删除等,32~126是打印字符,可以通过键盘输入并且能够显示出来。

2、GBK

        GBK全称是《汉字内码扩展范围》,是国家技术监督局为Window 95所制定的新的汉字内码范围,它的出现是为扩展GB2312,并加入更多的汉字。它的编码范围是8140~FEFE(去掉XX7F),总共有23940个码位,它能表示21003个汉字,它的编码是和GB2312兼容的,也就是说用GB2312编码的汉字可以用GBK来解码,并且不会有乱码。

3、UTF-16

        说到UTF必须提到Unicode(Universal Code 统一码),ISO试图创建一个全新的超语言字典,世界上所有的语言都可以通过这个字典来相互翻译。可想而知这个字典是多么复杂。

        UTF-16具体定义了Unicode字符在计算机中的存取方法。UTF-16用两个字节来表示Unicode的转化格式,它采用定长的表示方法,即不论什么字符都可以用两个字节表示。两个字节是16个bit,所以叫UTF-16。UTF-16表示字符非常方便,每两个字节表示一个字符,这就大大简化了字符串的操作,这也是Java以UTF-16作为内存的字符串存储格式的一个很重要的原因。

4、UTF-8

         UTF-16统一采用两个字节来表示一个字符,虽然在表示上非常方便、简单,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要用两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的情况下,这样会增大网络的传输的流量,而且也没必要。而UTF-8采用了一种变长技术,每个编码区域都有不同的字码长度。不同类型的字符可以由1~6个字节组成。

UTF-8有以下编码规则:

  • 如果是1个字节,最高位(第8位)为0,则表示这个1是ASCII字符(00~7F)。可见,所有ASCII编码已经是UTF-8了。
  • 如果是1个字节,以11开头,则连续的1的个数暗示这个字符的字节数,例如:110xxxxx代表它是双字节UTF-8字符的首字节。
  • 如果是1个字节,以10开始,表示它不是首字节,则需要向前查找才能得到当前字符的首字节。

3、Java Web中编码

          把整型数字1234567当作字符来存储,则采用UTF-8编码将会占用7个字节,采用UTF-16编码将会占用14个字节,但是把它当成int类型的数字来存储时则只需要4个字节。所以看一段文本的大小,只看字符本身的长度时没有意义的,即使时一样的字符,采用不同的编码最终存储的大小也会不同,所以从字符到字节一定要看编码类型。 

        当我们在计算机中的某个文本编辑器里输入某个汉字时,它到底时怎样表示的。我们知道,在计算机里所有的信息都是以0和1表示的,那么一个汉字,它到底是多少个0和1呢。我们能够看到的汉字都是以字符形式出现的,例如,在java中“淘宝”两个字符在计算机中的十进制数值是28120和23453,16进制数值是6bd8和5d9d,即这两个字符是由这两个数字唯一表示的。在java中一个char是16bit,相当于两个字节,所以两个汉字用char表示,在内存中会占用相当于4个字节的空间。

          把这两个问题搞清楚,我们来看下java web中哪些地方可能存在编码转换,列出如下4个:

1、URL中编码

URL的几个组成部分如下:

       get请求,URL的pathinfo(路径)和Query String(参数)的编码字符集不同,浏览器将URL中非ASCⅡ码字符按某种字符集转换为16进制到字符加上%。

  • 对URL的URI部分进行解码的字符集在<Connector URIEncoding="UTF-8"/>中定义,若没有定义则以默认编码ISO-8859-1解析。有中文URL时最好把URIEncoding设置为UTF-8编码。
  • QueryString的解码字符集要么是Header中ContentType定义的Charset,要么默认是ISO-8859-1,要使用ContentType中定义的编码,就要设置useBodyEncodingForURI:<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/>。这个配置项不是对整个URI都采用BodyEncoding编码,仅仅是对QueryString使用BodyEncoding解码。

2、HTTP Header编码

        客户端发起的HTTP请求除了URL外,还可能会在Header中传递其他参数(如Cookie)。对Header中的项进行解码默认使用ISO-8859-1,且不能设置Header其他的解码格式,若设置的Header中有非ASCII字符,解码中肯定会出现乱码。若一定要传递,则调用Tomcat中的URLEncoder编码,再添加到Header中,这样在从浏览器到服务器的传递过程中就不会丢失信息了。

3、POST表单的编解码

        POST表单的参数传递方式是通过HTTP的BODY传递到服务器的,当提交时先根据ContentType中的字符集进行解码,字符集编码可以由request.setCharacterEncoding(charset)来设置。

         此外务必注意:对POST表单提交参数的解码是发生在getParameter时,所以在第一次调用request.getParameter方法之前就要先设置request.setCharacterEncoding(charset)方法。

        关于上传的文件编码:也是使用ContentType定义的字符集编码,不过上传文件是以字节流的方式传输到服务器的本地临时目录,此过程尚不涉及字符编码,只有当文件内容添加到parameters时才进行编码。

4、HTTP BODY的编解码

         编解码字符集通过response.setCharacterEncoding来设置,通过Header的Content-Type返回客户端。若Header中没有Content-Type,浏览器会根据<meta http-equiv="Content-Type" content="text/html; charset=utf-8">中的charset来解码,若依然没有该属性,浏览器则使用默认编码。

         补充:使用JDBC来存取数据时要和数据的内置编码保持一致,可以设置JDBC URL来指定:jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章