`
lingqi1818
  • 浏览: 253415 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

字符编码出现乱码原因追寻

阅读更多
这几天做一个项目,基本上前台都是通过AJAX请求过来的,前端设计师用了雅虎的yui框架来封装JS。由于我们的系统一直采用GBK字符集编码,但是前端yui却只能通过utf-8编码把数据传过来,因为没有权限去修改框架级别的代码,所以只好跟ui约定好,在传送的数据中加一个_inut_charest=utf-8的参数,然后我在程序里恶心的硬编码进行convert。代码如下:

return new String(value.getBytes("GBK"),_inut_charest);


很顺利的就调试通过了,只是觉得有点恶心,不便以后维护。

但是下午突然出现一个奇怪的问题,如果参数里的中文是偶数个的话解码没有问题,但是单数的个的话就会出现乱码。比如汉字“你”就会成为“浣?”。
在网上查询答案,五花八门,大多数都没讲解原理,而且不太正确。在经过一番研究后终于找到了原因。

首先在这里简单介绍几种常见的字符集编码
ASCII编码采用单字节编码
GBK,GB18030,GB2312中文双字节编码
UTF-16双字节unicode编码
UTF-8变长多字节unicode编码,其中实践证明汉字为3个字节编码
IS08859系列,单字节编码

如果大家想详细了解各种编码的特点和产生的原因,请另外寻找资料,这里不做敷述。


下面我们来看一段代码:

import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;

/**
 * 类TestEncode.java的实现描述:测试转码类
 * 
 * @author ke.chenk 2009-3-13 下午09:01:34
 * @mail lingqi1818@msn.com
 */
public class TestEncode {

    /**
     * 本实例在中文windows下运行,故默认字符集为GBK
     * 
     * @param args
     */
    public static void main(String[] args) {
        testSingle();
        testDouble();
    }

    /**
     * 测试单字节编码(将GBK编码以UTF-8方式读取,然后再转为GBK)
     */
    private static void testSingle() {
        String s1 = encodeCovert("UTF-8", "GBK", "你");
        System.out.println(s1);
        printByteArray(s1.getBytes());
        System.out.println();
        String s2 = encodeCovert("GBK", "UTF-8", s1);
        System.out.println(s2);
        printByteArray(s2.getBytes());
    }

    /**
     * 测试双字节编码(将GBK编码以UTF-8方式读取,然后再转为GBK)
     */
    private static void testDouble() {
        String s1 = encodeCovert("UTF-8", "GBK", "你好");
        System.out.println(s1);
        printByteArray(s1.getBytes());
        System.out.println();
        String s2 = encodeCovert("GBK", "UTF-8", s1);
        System.out.println(s2);
        printByteArray(s2.getBytes());
    }

    private static String encodeCovert(String srcCharset, String desCharset, String value) {
        if (value == null) {
            return null;
        }
        try {
            if ("".equals(srcCharset) || srcCharset == null) {
                return new String(value.getBytes(), desCharset);
            }
            if ("".equals(desCharset) || desCharset == null) {
                return new String(value.getBytes());
            }
            return new String(value.getBytes(srcCharset), desCharset);
        } catch (UnsupportedEncodingException e) {
            System.out.println(MessageFormat.format(
                                                    "convert encode fail srcCharset is {0} , desCharset is {1} , value is {2}",
                                                    srcCharset, desCharset, value));
            return value;
        }
    }

    private static void printByteArray(byte[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + ",");
        }
    }

}



运行结果如下:

浣?
-28,-67,63,
??
63,63,浣犲ソ
-28,-67,-96,-27,-91,-67,
你好
-60,-29,-70,-61,

由此可见,当我们的字符以较少字节的编码方式,被错误的读取成较大字节的编码,然后在转码成较少字节的编码方式后就会丢失数据。
比如汉字“你”,正常的十进制UTF-8,byte数组应该为[-28,-67,-96],但是进行GBK编码的时候,由于GBK是2字节方式编码,所以-28,-67构成了汉字“浣”,-96找不到对应编码方式,于是就加上了?的ASCII码63.但是汉字“你好”转成UTF-8刚好为-28,-67,-96,-27,-91,-67,是偶数个,并且2个2个的字符刚好在GBK中有对应编码,所以欺骗了编码规则,数据没有被替换,这样才正常解析回了UTF-8编码


结论:
1.任何的乱码产生都是有原因可以查询的。
2.只要以正确的方式读取原来的数据,再进行编码是不会产生丢失数据或者乱码的情况的。
3.GBK->UTF-8->GBK这个过程如果是单个汉字100%出问题,其他情况也可以类推。

解决方案:
1.整个应用统一编码格式就不会出现这个问题。
2.对参数以URLENCODING方式进行传输,但是这样在传输的时候会增加数据量。
3.在我们这个CASE中,前端直接以GBK方式传输数据,不要转为UTF-8


=============================================================

补充:
搞了半天,原来问题出在我们自己这里,看一段代码:


前台代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 

"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>Insert title here</title>
</head>
<body>
<input type="text" id="testinput"/>
<input type="button" onclick="test();" value="提交"/>
</body>
</html>
<script language="JavaScript" type="text/javascript">
function test(){
//创建浏览器兼容的XMLHttpRequest对象
var xmlhttp;
try{
	xmlhttp= new ActiveXObject('Msxml2.XMLHTTP');
}catch(e){
	try{
		xmlhttp= new ActiveXObject('Microsoft.XMLHTTP');
	}catch(e){
		try{
			xmlhttp= new XMLHttpRequest();
		}catch(e){}
	}
}
//定义XMLHttpRequest对象的事件处理程序
xmlhttp.overrideMimeType("text/html;charset=UTF-8");
xmlhttp.onreadystatechange=function(){
	if(xmlhttp.readyState==4){
		if(xmlhttp.status==200){
			alert(xmlhttp.responseText);
		}else{
			alert(xmlhttp.status);
		}
	}
}
//创建一个连接
xmlhttp.open("post","/web/servlet/TestServlet");
xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); 
//发送请求
xmlhttp.send("myparam="+document.getElementById("testinput").value);
}
</script>

后台代码:

response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        request.setCharacterEncoding("UTF-8");
        System.out.println(request.getCharacterEncoding());
        String s = request.getParameter("myparam");
        System.out.println("pure s ->"+s);
        System.out.println(new String(s.getBytes("UTF-8"),"GBK"));
        byte[] temp = s.getBytes("UTF-8");
        for (int i = 0; i < temp.length; i++) {
        System.out.println(temp[i]+",");
        }
        System.out.println("===============================");
        String[] charsets = { "GBK", "GB2312", "8859_1", "UTF-8", "UTF-16" };
        for (int i = 0; i < charsets.length; i++) {
            for (int j = 0; j < charsets.length; j++) {
                if (i != j) {
                    String ss = new String(s.getBytes(charsets[i]), charsets[j]);
                    System.out.println("i="+i+",j="+j+"---"+ss);
                }
            }
        }



打印结果如下:
UTF-8
pure s ->你
浣?
-28,
-67,
-96,
===============================
i=0,j=1---你
i=0,j=2---??
i=0,j=3---??
i=0,j=4---?
i=1,j=0---你
i=1,j=2---??
i=1,j=3---??
i=1,j=4---?
i=2,j=0---?
i=2,j=1---?
i=2,j=3---?
i=2,j=4---?
i=3,j=0---浣?
i=3,j=1---浣?
i=3,j=2---???
i=3,j=4---?
i=4,j=0---??O`
i=4,j=1---??O`
i=4,j=2---??O`
i=4,j=3---??O`

可见,前面我们的分析原理的思路是正确的,但是乱码产生的原因不是js转码产生的,其实js会正确的将页面的GBK编码转换成为UTF-8,然后我们的框架会根据_input_charset=UTF-8来设置request.setCharacterEncoding("UTF-8");这样当我们从request.getParameter("param")的时候已经是正确的中文了,由于一开始我们没有加_input_charset=UTF-8这个参数,所以一直采用硬编码进行转换,结果当编码正确的时候我们又重新进行了转换,才有了GBK->UTF-8->GBK的步骤,乱码也就随之产生了。
2
0
分享到:
评论

相关推荐

    java字符集编码乱码详解

    ### Java字符集编码乱码详解 #### 一、编码与乱码基础知识 在计算机科学领域,字符集(Character Set)是指一系列符号和电子通信代码的标准集合。每种字符集都有其特定的应用场景和优势。例如,ASCII(American ...

    winform的字符串转换乱码解决

    在Windows Forms(Winform)应用开发中,遇到字符串转换出现乱码的问题是非常常见的。这通常涉及到字符编码的不一致或不正确处理。本篇将详细解释这个问题的原因,并提供解决方案。 一、乱码产生的原因 1. 文件...

    MySQL字符编码及乱码解决方案.txt

    MySQL字符编码及乱码解决方案 · 使用多种字符集来存储字符串 · 使用多种校对规则来比较字符串 · 在同一台服务器, 同一个数据库或甚至在同一个表中使用不同字符集或校对规则来混合字符串 · 允许定义任何级别...

    delphi中如何解决Combobox删除字符时出现乱码

    3. **代码处理不当**:在用户删除字符时,如果自定义了事件处理程序,比如OnChange或OnEditChange事件,那么在这些事件中处理字符串时的编码错误可能导致乱码。检查相关代码,确保字符串操作符合当前的编码环境。 4...

    关于GBK和Unicode字符集转换乱码问题

    本文将深入探讨GBK与Unicode这两种字符集之间的转换问题,特别是在转换过程中出现乱码的原因及解决方法。 #### GBK与Unicode简介 - **GBK**(GB2312-80的扩展):这是一种简体中文字符集标准,由中华人民共和国...

    java字符编码监听器

    在Java Servlet规范中,提供了`SetCharacterEncodingFilter`这样的过滤器,用于确保请求参数和响应内容的正确编码,避免因为编码不一致导致的数据乱码问题。 1. **字符编码的重要性** 在网络通信中,字符编码扮演...

    jdbc连接oracle字符集不同出现乱码

    综上所述,当使用 JDBC 连接 Oracle 数据库并遇到字符集不同导致的乱码问题时,可以通过调整 SQL 语句的编码或结果集的编码来解决。这两种方法都可以有效地避免乱码问题的发生,但在实际应用中应根据具体情况选择最...

    Java Web程序开发中字符乱码的原因与解决办法.pdf

    为了解决这个问题,我们首先要了解字符集和字符编码的相关知识,然后分析字符乱码的原因,并最终找出相应的解决办法。 字符集是字符的集合,它包含了计算机能够处理的文本数据。常见的字符集有ASCII字符集、Unicode...

    字符编码文档

    本文将深入探讨字符与编码的概念、发展历史、程序中的应用,以及如何解决乱码问题和进行编码转换。 **一、字符与编码的概念** 字符是文字、数字、标点符号等可视元素的统称,而编码则是为每个字符分配一个唯一的...

    Python字符编码_中文乱码.pdf

    ### Python字符编码与中文乱码问题详解 #### 一、引言 在处理中文文本时,经常遇到的一个问题是中文乱码。特别是在使用Python进行开发时,由于不同的操作系统默认编码方式不同,以及Python自身对源文件编码的处理...

    C#(.net)中按字节数截取字符串最后出现乱码问题的解决

    最近需要用到按字节数截取字符串。在网上找了很多方法。 Encoding.Default.GetString采用的Default Encoding.UTF8.GetBytes采用的是utf-8编码。这样当然是乱码。尤其出现中文时候。 对这类数据处理当然要用统一的...

    解决字符编码的过滤器

    例如,当用户提交包含中文或其他非英文字符的数据时,如果没有正确的字符编码设置,就可能会出现乱码现象。这不仅影响用户体验,还可能导致数据丢失或错误处理等问题。 #### 三、Struts2框架简介 Struts2是一个...

    字符编码查询之星

    在信息化时代,字符编码的兼容性和准确性是数据交换的重要环节,这款软件的出现,无疑为处理多样的编码格式提供了便捷的解决方案。 首先,我们来了解一下字符编码的基本概念。字符编码是一种规则,它将字符(如字母...

    字符编码查询工具.rar

    总结来说,"字符编码查询工具"是一个实用的应用,对于需要处理不同字符编码的IT专业人士来说,它是一个强大的辅助工具,能帮助他们快速了解和转换各种字符编码,从而解决可能出现的编码相关问题。通过这个工具,用户...

    网页字符编码问题总结及解决

    3. **文件保存格式错误**:编辑器默认保存的编码格式与实际需要的编码格式不符,导致文件读取时出现乱码。 #### 三、解决乱码的方法 1. **确保HTML文档中声明正确的字符编码**: ```html ;charset=UTF-8"&gt; ``` ...

    impala中substr()截取中文字符串乱码的问题

    然而,当涉及到处理中文字符时,Impala的内置函数`substr()`和`substring()`可能会遇到一些挑战,尤其是在截取中文字符串时可能出现乱码问题。这是因为这两个函数在设计时可能没有充分考虑多字节字符集,如UTF-8,而...

    工具-字符编码转换

    3. 解决编程或网站开发中遇到的编码问题,例如在读取非UTF-8编码的文本文件时出现乱码。 4. 对于需要在多种编码系统之间交换数据的用户,如XML、CSV或JSON文件,转换工具可以确保数据的完整性。 在实际使用中,用户...

    字符编码

    在Java中,字符编码问题往往出现在输入输出流的处理、字符串的转换或者网络通信中。 在IT领域,"源码"和"工具"这两个标签表明这个话题可能包含了代码示例和实用工具来帮助理解和解决字符编码问题。源码可能是指Java...

    java_字符编码 Javajava_字符编码问题

    ### Java 字符编码详解 #### 一、Java 字符编码基础概念 ...正确的字符编码管理不仅可以避免乱码问题,还能提高程序的健壮性和可维护性。希望本文能帮助开发者们更好地理解和处理 Java 中的字符编码问题。

Global site tag (gtag.js) - Google Analytics