一、为什么要编码?

由于人类的语言太多,因而表示这些语言的符号太多,无法用计算机的一个基本的存储单元----byte来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解。

byte一个字节即8个bit,所以能表示的字符范围是0~255个,这满足不了人类的需要,要解决这个矛盾必须需要一个新的数据结构char,从char到byte必须经过编码。

二、常用编码介绍

ASCII码

总共128个,用一个字节的低7位表示,0~31是控制字符,如换行、回车、删除等,32~126是打印字符,可以通过键盘输入并且能够显示出来.

ISO-8859-1

扩展自ASCII,仍然是单字节编码,一共能表示256个字符

GB2312

双字节编码。总编码范围A1~F7 ,其中A1~A9是符号区,包含682个符号,从B0~F7是汉字区,包含6763个汉字

GBK

扩展自GB2312,能表示21003个汉字,其编码和GB2312是兼容的。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符

GB18030

在实际应用系统中使用的并不广泛

Unicode

Unicode 是 Java 和 XML 的基础,使用0~65 535的双字节无符号数对每一个字符进行编码

UTF-8

是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码,使用Unicode编码,一个英文字符要占用两个字节,在Internet上,大多数的信息都是用英文来表示的,如果都采用Unicode编码,将会使数据量增加一倍。为了减少存储和传输英文字符数据的数据量,可以使用UTF-8编码。

GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:
GBK、GB2312--Unicode--UTF8
UTF8--Unicode--GBK、GB2312

三、对乱码产生过程的分析

为了让使用Java语言编写的程序能在各种语言的平台下运行,Java在其内部使用Unicode字符集来表示字符,这样就存在Unicode字符集和本地字符集进行转换的过程。当在Java中读取字符数据的时候,需要将本地字符集编码的数据转换为Unicode编码,而在输出字符数据的时候,则需要将Unicode编码转换为本地字符集编码。
例如,在中文系统下,从控制台读取一个字符“中”,实际上读取的是“中”的GBK编码0xD6D0,在Java语言中要将GBK编码转换为Unicode编码0x4E2D,此时,在内存中,字符“中”对应的数值就是0x4E2D,当我们向控制台输出字符时,Java语言将Unicode编码再转换为GBK编码,输出到控制台,中文系统再根据GBK字符集画出相应的字符。
从上述过程来看,读取和写入的过程是可逆的,那么理应不会出现中文乱码问题。然而,实际应用的情形,比上述过程要复杂得多。在Web应用中,通常都包括了浏览器、Web服务器、Web应用程序和数据库等部分,每一部分都有可能使用不同的字符集,从而导致字符数据在各种不同的字符集之间转换时,出现乱码的问题。
在Java语言中,不同字符集编码的转换,都是通过Unicode编码作为中介来完成的。例如,GBK编码的字符“中”要转换为ISO-8859-1编码,其过程如下:
(1)因为在Java中的字符,都是用Unicode来表示的,所以GBK编码的字符“中”要转换为Unicode表示:0xD6D0->0x4E2D。
(2)将字符“中”的Unicode编码转换为ISO-8859-1编码,因为Unicode编码0x4E2D在ISO-8859-1中没有对应的编码,于是得到0x3f,也就是字符“?”。

下面的代码演示了这一过程:

  1. 1 /GBK编码的字符“中”转换为Unicode编码表示
  2. 2 String str="中";
  3. 3 //将字符“中”的Unicode编码转换为ISO-8859-1编码
  4. 4 byte[] b=str.getBytes("ISO-8859-1");
  5. 5 for(int i=0;i<b.length;i++) {
  6. 6 //输出转换后的二进制代码。
  7. 7 System.out.print(b[i]);
  8. 8 }

当从Unicode编码向某个字符集转换时,如果在该字符集中没有对应的编码,则得到0x3f(即问号字符?)。这就是为什么有时候我们输入的是中文,在输出时却变成了问号。

从其他字符集向Unicode编码转换时,如果这个二进制数在该字符集中没有标识任何的字符,则得到的结果是0xfffd。例如一个GBK的编码值0x8140,从GB2312向Unicode转换,然而由于0x8140不在GB2312字符集的编码范围(0xa1a1-0xfefe),当然也就没有对应任何的字符,所以转换后会得到0xfffd。

下面的代码演示了这一过程。

  1. 1 // 构造一个二进制数据。
  2. 2 byte[] buf = { (byte) 0x81, (byte) 0x40, (byte) 0xb0, (byte) 0xa1 };
  3. 3 // 将二进制数据按照GB2312向Unicode编码转换。
  4. 4 String str = new String(buf, "GB2312");
  5. 5 for (int i = 0; i < str.length(); i++) {
  6. 6 // 取出字符串中的每个Unicode编码的字符。
  7. 7 char ch = str.charAt(i);
  8. 8 // 将该字符对应的Unicode编码以十六进制的形式输出。
  9. 9 System.out.print(Integer.toHexString((int) ch));
  10. 10 System.out.print("--");
  11. 11 // 输出该字符。
  12. 12 System.out.println(ch);
  13. 13 }

四、web开发避免中文乱码

1.Jquery的get、 post方式提交中文乱码

Ajax方式提交,如果参数中带有中文参数,最好就是指定页面格式的编码.Jquery这里默认使用utf-8的编码

值得注意的是: 在$.get()、$.post()方式中,要指定内容返回的内容的contentType格式.

  1. 1 get方法传文字字符串就会有乱码,因为是通过url传参的。
  2. 2 所以你要在js客户端经过2次转码,同样服务器端也要转码。
  3. 3 $.get("AjaxService?userName=" + encodeURI(encodeURI(userName)), null, function (data) {
  4. 4 $("#result").html(data);
  5. 5 });
  6. 6 2个效果是一样的:encodeURIComponent(userName) encodeURI(encodeURI(userName))
  7. 7 服务器端转码:String userName = URLDecoder.decode(request.getParameter("userName"), "UTF-8");

因为应用服务器会自动帮你做一次URLDecode,所以再加上你自己在代码里面写的URLDecode,一共就是两个Decode了

一般情况下, 发送 encodeURIComponent(parmeName)+"="+encodeURIComponent(parmeValue);
接收时, 直接 String paramValue = request.getParameter(paramName); // 容器自动解码.
我们知道 encodeURIComponent 使用的是 UTF-8 编码规则来编的.
如果 request.getParameter(paramName) 时,容器也按 UTF-8 解的话,是正确的. 根本无须在客户端
进行二次的 encodeURIComponent(...)
如果 request.getParameter(paramName),容器没有按 UTF-8 解的话, 结果只有一个,就是乱码!
容器按什么编码来解码,决定于 request.setCharacterEncoding(***) 或者 服务器程序配置.
如果你在 jsp 程序中,能够 request.setCharacterEncoding("UTF-8"), 并且 修改服务器配置,让容器在解 GET 提交的参数时,使用 UTF-8.
客户端提交前不用二次编码, 接收时,也只要直接 request.getParameter(paramName) 即可

Java、escape(str)和unescape(str);

对String对象进行编码或者解码,以便它们能在所有计算机上可读;str中的非ASCII字符都是用【%xx】来表示的,其中xx表示该字符的16进制数,例如空格返回的是”%20”,字符值大于255的以%uxxxx格式存储

注意:escape()方法不能够对统一资源标识码(URI)进行编码,对其编码应使用encodeURI()和encodeURIComponent()方法;

unescape()方法不能解码URI,解码需使用decodeURI()和decodeURIComponent();

这里建议参考Commons-lang3包中的StringEscapeUtils

2.JSP与页面参数传参乱码

(1)页面编码不一致

  1. 1 <%@ page contentType="text/html; charset=gb2312"%>
  2. 2 ...
  3. 3 <meta http-equiv="Content-Type" content="text/html charset=gb2312">
  4. 4 ...

使用Servlet规范中的过虑器指定编码,过滤器的在web.xml中的典型配置和主要代码如下:

web.xml:

  1. 1 <filter>
  2. 2 <filter-name>CharacterEncodingFilter</filter-name>
  3. 3 <filter-class>cn.com.tony.web.CharacterEncodingFilter</filter-class>
  4. 4 <init-param>
  5. 5 <param-name>encoding</param-name>
  6. 6 <param-value>GBK</param-value>
  7. 7 </init-param>
  8. 8 </filter>
  9. 9 <filter-mapping>
  10. 10 <filter-name>CharacterEncodingFilter</filter-name>
  11. 11 <url-pattern>/*</url-pattern> </filter-mapping>

CharacterEncodingFilter.java代码段:

  1. 1 public class CharacterEncodingFilter implements Filter {
  2. 2 protected String encoding = null;
  3. 3 public void init(FilterConfig filterConfig) throws ServletException {
  4. 4 this.encoding = filterConfig.getInitParameter("encoding");
  5. 5 }
  6. 6 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  7. 7 request.setCharacterEncoding(encoding);
  8. 8 response.setContentType("text/html;charset="+encoding);
  9. 9 chain.doFilter(request, response);
  10. 10 }
  11. 11 }

或者使用框架提供的乱码过滤器如Spring

  1. 1 <filter>
  2. 2 <filter-name>EncodingFilter</filter-name>
  3. 3 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  4. 4 <init-param>
  5. 5 <param-name>encoding</param-name>
  6. 6 <param-value>GBK</param-value>
  7. 7 </init-param>
  8. 8 <init-param>
  9. 9 <param-name>forceEncoding</param-name>
  10. 10 <param-value>true</param-value>
  11. 11 </init-param>
  12. 12 </filter>
  13. 13 <filter-mapping>
  14. 14 <filter-name>EncodingFilter</filter-name>
  15. 15 <url-pattern>/*</url-pattern>
  16. 16 </filter-mapping>

3.链接传参乱码

在传参的jsp对中文进行编码:href="new.jsp?name=java.net.URLEncoder.encode("链接")";

在接受的jsp对中文进行转码:String str = URLDecoder.decode(request.getParameter("name "), "utf-8");

读取的编码格式要跟页面中设置的编码格式一致。

4.网页编码格式设置:

(1)、指定文件的存储编码,很明显,该设置应该置于文件的开头。例如:<%@page pageEncoding="GBK"%>,正常显示中文,如果不设置默认是iso8859-1,它是不支持中文的。

(2)、jsp输出,即:browser显示网页的时候,首先使用response.setCharacterEncoding()中指定的编码,也可以是<%@ page contentType="text/html; charset= GBK" %>。如果未指定,则会使用网页中meta项指定中的contentType。

(3)、 meta设置

指定网页使用的编码,该设置对静态网页尤其有作用。因为静态网页无法采用jsp的设置,而且也无法执行response.setCharacterEncoding()。例如:<META http-equiv="Content-Type" content="text/html; charset=GBK" />而在jsp中meta的优先级最低,没有以上两种的编码时才采用这中编码推荐只用utf-8,它支持所有字符。

若是Servlet显示网页就用response.setContentType("text/html;charset=utf-8");

5.数据库读取乱码

大部分数据库都支持以unicode编码方式,所以解决Java与数据库之间的乱码问题比较明智的方式是直接使用unicode编码与数据库交互。 很多数据库驱动自动支持unicode,其他大部分数据库驱动,可以在驱动的url参数中指定,如 mysql驱动:jdbc:mysql://localhost/MYTEST?useUnicode=true&characterEncoding=GBK。

6.浏览器编码异常

乱码问题

  1. 1 <%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%>
  2. 2 ......
  3. 3 <%
  4. 4 //*****写在首行*****//
  5. 5 request.setCharacterEncoding("UTF-8");
  6. 6 ...
  7. 7 %>

在浏览器中,中文通过url传递都会默认被编码,所以在url中的中文还必须编码后再使用,

使用 <form>...</form> 提交的,浏览器才会编码.

<form>提交时,浏览器使用什么编码编,决定于 form 的 accept-charset 属性(标准浏览器) 或者 document.charset(IE)
<form accept-charset="...." >...</form>
提交 application/x-form-www-encoded 表单时,浏览器 把所有参数 按  key=value 的形式组合
分别对 key, value 进行编码.
多个参数间,用 "&" 连接, 如: key2=%E4%B8%AD%E6%96%87&key=AAAA&key=BBB&key1=CCC&%E6%B1%89%E5%AD%97=%E4%B8%AD%E6%96%87
ajax 提交的,需要自己手动设置编码

浏览器url编码
eg : "2014中华小当家"先编码成utf-8,再在url地址栏中传递,ie10地址栏输入中文不会乱码,火狐和chrome会

7.通过参数设置接口编码

WebUtils.java

  1. 1 public static String getQueryValue(String queryString,String key){
  2. 2 if(queryString==null || queryString.length()==0)return null;
  3. 3 int reqIdx = queryString.indexOf(key);//req_enc=utf-8&resp_enc=gbk
  4. 4 String enc = null;
  5. 5 if(reqIdx!=-1){
  6. 6 reqIdx = reqIdx+key.length();
  7. 7 int endIdx = reqIdx;
  8. 8 for (;
  9. 9 endIdx < queryString.length() && queryString.charAt(endIdx)!='&';
  10. 10 endIdx++) {
  11. 11 }
  12. 12 if(endIdx>reqIdx){
  13. 13 enc = queryString.substring(reqIdx,endIdx);
  14. 14 if(!enc.equalsIgnoreCase("gbk")
  15. 15 && !enc.equalsIgnoreCase("utf-8")){
  16. 16 enc = null;
  17. 17 }else{
  18. 18 System.err.println("error :"+key+" is "+ enc);
  19. 19 }
  20. 20 }
  21. 21 }
  22. 22 return enc;
  23. 23 }

setCharset.jsp

  1. 1 <%@ page contentType="text/html;charset=GBK" %><%
  2. 2 String queryString = request.getQueryString();
  3. 3 String reqEnc = getQueryValue(queryString,"req_enc=");
  4. 4 if(reqEnc!=null){
  5. 5 request.setCharacterEncoding(reqEnc);
  6. 6 }
  7. 7 String respEnc = getQueryValue(queryString,"resp_enc=");
  8. 8 if(respEnc!=null){
  9. 9 response.setCharacterEncoding(respEnc);
  10. 10 }
  11. 11 %>

五、总结

上面提到的方法应该能解决大部分乱码问题,如果在其他地方还出现乱码,可能需要手动修改代码。解决Java乱码问题的关键在于在字节与字符的转换 过程中,你必须知道原来字节或转换后的字节的编码方式,转换时采用的编码必须与这个编码方式保持一致,否则将会出现Java文乱码。

参考:

浏览器url编码

解决JSP中文乱码问题

注:本文来注:本文来源于:cnblogs:牛奶、不加糖

Java编码与乱码问题的更多相关文章

  1. java编码解码乱码问题

    服务器设值(中文)到界面使用了两次编码: String pageJson=URLEncoder.encode(URLEncoder.encode(str,"GBK"), " ...

  2. java中文乱码解决之道(四)-----java编码转换过程

    前面三篇博客侧重介绍字符.编码问题,通过这三篇博客各位博友对各种字符编码有了一个初步的了解,要了解java的中文问题这是必须要了解的.但是了解这些仅仅只是一个开始,以下博客将侧重介绍java乱码是如何 ...

  3. java中文乱码解决之道(四)—–java编码转换过程

    原文出处:http://cmsblogs.com/?p=1475 前面三篇博客侧重介绍字符.编码问题,通过这三篇博客各位博友对各种字符编码有了一个初步的了解,要了解java的中文问题这是必须要了解的. ...

  4. Java:编码与乱码问题

    一.为什么要编码? 由于人类的语言太多,因而表示这些语言的符号太多,无法用计算机的一个基本的存储单元----byte来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解. byte一个字节即8 ...

  5. 深度剖析java编码,彻底解决java乱码问题_1

    理解: 1,Java编译器(即编译成class文件时) 用的是unicode字符集. 2,乱码主要是由于不同的字符集相互转换导致的,理论上各个字符的编码规则是不同的,是不能相互转换的,所以根本解决乱码 ...

  6. 【Java基础专题】编码与乱码(05)---GBK与UTF-8之间的转换

    原文出自:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html 在很多论坛.网上经常有网友问" 为什么我使用 ...

  7. java编码,乱码问题详解

    一.常见的编码格式 1.ASCII 基础编码,英文和西欧字符. 用一个字节的低7位表示,一共128个. 0~13是控制字符如换行.回车.删除等,32~126是打印字符,键盘输入. 2.IOS-8859 ...

  8. 【JAVA编码专题】总结

    第一部分:编码基础 为什么需要编码:用计算机看得懂的语言(二进制数)表示各种各样的字符. 一.基本概念 ASCII.Unicode.big5.GBK等为字符集,它们只定义了这个字符集内有哪些字符,以及 ...

  9. 【JAVA编码专题】深入分析 Java 中的中文编码问题

    http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/ 几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是为什么 ...

随机推荐

  1. checkBox全选全不选及数据提交后台

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  2. Error configuring application listener of

    最近在做spring+struts2+IbatIS的项目,昨天eclipse启动服务器正常,结果今天来了就总是报错,错误如下:严重: Error configuring application lis ...

  3. 窗体监听事件WindowListener

    EXIT_ON_CLOSE:结束窗口所在的应用程序.在窗口被关闭的时候会退出JVM. DISPOSE_ON_CLOSE:隐藏当前窗口,并释放此窗体占有的资源.如果程序没有其他线程在运行,当所有窗口都被 ...

  4. 四个修改Docker默认存储位置的方法

    方法一.软链接 默认情况下Docker的存放位置为:/var/lib/docker 可以通过下面命令查看具体位置: sudo docker info | grep "Docker Root ...

  5. Linux用户密码策略

    使用Linux快三年了,从未想过Linux用户密码策略,从未把一本Linux的书从头看到尾(上学时的教材除外),故不知书上有无介绍,直到最近参加公司的信息安全稽核会议后才开始考虑Linux用户密码策略 ...

  6. sklearn11_函数汇总

    sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...

  7. C#设计模式(15)——迭代器模式

    1.迭代器模式介绍 迭代器模式主要用于遍历聚合对象,将聚合对象的遍历行为分离出来,抽象为一个迭代器来负责.迭代器模式用的十分普遍,C#/JAVA等高级语言都对迭代器进行了封装用于遍历数组,集合,列表等 ...

  8. 运用Zabbix实现内网服务器状态及局域网状况监控(1) —— Zabbix介绍

    一.Zabbix简介 Zabbix 是一个企业级的.开源的.分布式的监控套件 Zabbix 可以监控网络和服务的监控状况. Zabbix 利用灵活的告警机制,允许用户对事件发送基于 Email 的告警 ...

  9. JavaScript基本操作之——九个 Console 命令

    一.显示信息的命令 console.log('hello'); console.info('信息'); console.error('错误'); console.warn('警告'); 二.占位符 c ...

  10. 从池子里的beta看秋香, 个性迥异

    从池子里的beta看秋香, 个性迥异 前文里那十只个股为例, 统计了它们的beta值. 回顾如下: Num name code Beta 0 深圳燃气 601139 0.710 公用事业 1 分众传媒 ...