zoukankan      html  css  js  c++  java
  • 深入分析Java Web中的中文编码问题

    编码的原因

    需要编码的原因可以总结为以下几条:

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

    所以char和byte之间必须编码

    常见编码格式

    目前的编码格式很多,如GB2312,GBK,UTF-8,UTF-16都可以表示汉字,那么使用哪种编码格式存储汉字呢?就需要考虑其他因素了,例如:是存储空间重要还是编码的效率重要

    ASCII码

    ASCII码共有128个,使用1个字节的低7位表示,0-31是控制字符如回车,换行,删除等,32-126是打印字符,可以通过键盘输入并显示出来

    ISO-8859-1

    ISO-8859-1在ASCII码基础上进行了拓展,可以覆盖大多数西欧字符。ISO-8859-1仍然是单字符编码,它总共有256个字符

    ASCII码和ISO-8859-1都无法表示中文汉字。如果在ISO-8859-1中编码中文会出现"?"

    GB2312

    GB2312是双字节编码,包含628个字符和6763个汉字

    GBK

    GBK是为了拓展GB2312,加入了更多个汉字,可以表示21003个汉字。它的编码和GB2312兼容,也就是说GB2312编码的汉字可以用GBK来解码,不会出现乱码

    GBK和GB2312都是双字节的编码,对于单字节的(如英文,空格,控制符等)使用1个字节编码,汉字使用2个字节

    GBK和GB2312转码过程中都是需要查询码表,从效率上来说,因为GBK表示的字符更多,所以效率要略低于GB2312

    GB18030

    GB18030是国家标准,它的编码也与GB2312兼容,但是使用并不广泛

    UTF-16

    UTF就是Unicode(Universal Code 统一码),ISO试图创建一个全新的超语言字典,世界上所有的语言都可以通过这个字典互相翻译。

    UTF-16固定采用两个字节表示Unicode的转化格式,采用定长的方法,无论深入字符都采用两个字符表示,两个字节即16个bit,所以叫UTF-16

    定长的方式使得转码很快(因为格式固定,规则简单),但浪费了空间。Java内存中的编码使用的就是UTF-16

    UTF-8

    UTF-8采用一种变长技术,每个区域有不同的字码长度,不同的字符采用1~6个字节表示。英文采用1个字节,汉字采用3个字节

    UTF-16采用顺序编码,不能对单个字符的编码值进行校验,如果中间一个字符值损坏,后面所有的码值都会受到影响,而UTF-8不存在这样的问题

    UTF-8编码与GBK和GB2312不同,不用查码表,所以UTF-8编码效率要高于它们,所以在存储中文字符时采用UTF-8比较理想

    在几种编码格式比较

    GBK和GB2312规则类似,但是GBK范围更大,能够处理大多数的汉字字符,所以GBK和GB2312进行比较,应该选择GBK

    UTF-16和UTF-8都是处理Unicode编码,但是它们编码规则不同,相对而言UTF-16编码效率要高,从字符到字节之间互相转换更简单,适合在磁盘和内存之间使用,可以进行字符和字节之间快速切换,如Java内存编码就是使用的UTF-16编码。但是它不适合在网络之间传输,因为网络传输容易损坏字节流,一旦字节流损坏很难恢复,所以UTF-8更适合网络传输

    UTF-8对ASCII码采用单字节存储,另外单个字符出现损坏才会影响后面的其他字符。在编码效率上介于GBK和UTF-16字节,在编码效率上和编码安全上做了平衡,应优先使用

    Java中采用编码的场景

    在I/O操作存在的编码

    涉及到编码的地方一般都在字符到字节或者字节到字符的场景上,这种转换场景主要是I/O,包括磁盘I/O和网络I/O

    InputStreamReader类负责在I/O过程中字节到字节的转换

    OutputStreamWriter类负责在I/O过程中字符到字节的转换

    在使用这两个类时推荐使用两个参数的构造,并在第二个参数指定编码集

    String charset = "UTF-8";
    new InputStreamReader(inputStream, charset);
    new OutputStramWriter(outputStream, charset);
    

    只要使用统一编码Charset编码集,一般都不会出现乱码

    强烈不建议使用操作系统默认的编码,即构建对象时候不指定编码集,因为这样让程序的编码环境和运行环境绑定,在跨环境时很可能出现乱码问题

    在内存操作中的编码

    除了I/O操作,还有就是在内存中进行字符到字节的数据类型转换。在Java中用String表示字符串,所以String类提供了转换的方法

    String s = "中文字符串";
    byte[] b = s.getBytes("UTF-8");
    String str = new String(b, "UTF-8");
    

    Java Web中涉及到的编码

    Get请求:url中的中文会以"%"+编码的形式出现,是因为浏览器会将非ASCII码字符编码成16进制,并在16进制字节前加上"%"。解决方法:一是修改web容器(如Tomcat,Tomcat8及之后的版本默认用的UTF-8,低版本使用的是ISO-8859-1)默认的URIEncoding,再就是在后端代码中获取到参数之后手动解码(在Java端有处理URL编码两个类:java.net.URLEncodingjava.net.URLDecoding);二者推荐使用后者,但是强力建议不要在Get请求中传输中文

    POST请求:请求体中的中文参数可以通过request.setCharacterEncoding方法设置后,获得的参数就是中文了。一般来说框架中都会通过一个Filter的方式设置,开发时无需关心POST中的中文,但是对于GET中的中文则不适用,所以再次强力建议不要在Get请求中传输中文参数

    HTML页面通过meta设置编码,HTML5则默认使用UTF-8

    数据库通过客户端的JDBC驱动完成连接,JDBC驱动通过JDBC URL设置编码,如MySQL:url=jdbc:mysql:jdbc:///DB?userUnicode=true&characterEncoding=UTF-8

    引入外部JS文件时,指定该文件的编码:<scripte src="" charset="utf-8">

    XML,JSP,Velocity指定编码格式

    <?xml version="1.0" encoding="UTF-8"?>
    
    <%@page contentType="text/html;charset=UTF-8"%>
    
    services.VelocityService.input.encoding=UTF-8
    
  • 相关阅读:
    [转]SVN服务器搭建和使用(二)
    [转]SVN服务器搭建和使用(一)
    BZOJ 2049 Sdoi2008 Cave 洞穴勘测
    BZOJ 1589 Usaco2008 Dec Trick or Treat on the Farm 采集糖果
    BZOJ 2796 POI2012 Fibonacci Representation
    BZOJ 2115 Wc2011 Xor
    BZOJ 3105 CQOI2013 新Nim游戏
    BZOJ 2460 Beijing2011 元素
    BZOJ 3687 简单题
    BZOJ 1068 SCOI2008 压缩
  • 原文地址:https://www.cnblogs.com/lz2017/p/13771943.html
Copyright © 2011-2022 走看看