`
lingqi1818
  • 浏览: 252211 次
  • 性别: 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
分享到:
评论

相关推荐

    winform的字符串转换乱码解决

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

    解决JavaEE开发中字符编码出现乱码的问题

    在JavaEE开发中,字符编码问题是一个非常常见且容易出现的问题,其中最常见的问题之一就是乱码问题。本文将从三个主要的地方进行编码设置,以解决JavaEE开发中字符编码出现乱码的问题。 首先,我们需要关注的是HTML...

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

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

    VB转换字符编码(构造发送字符消息的准备工作)

    在VB中,当你需要在窗体间传递字符消息时,必须确保字符编码的一致性,否则可能会出现乱码问题。这是因为VB默认使用的是ANSI编码,而在某些情况下,如接收到的是Unicode编码的消息,就需要进行相应的转换。 以下是...

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

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

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

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

    解决gb2312编码导致乱码问题

    gb2312是一种较老的中文字符编码标准,主要用于简体中文,而在现代的网络环境中,UTF-8编码已经成为主流,这可能导致在不兼容的情况下出现乱码。 描述中提到的“使用方法见本人博客《IE中js往后台传输中文乱码》”...

    ubuntu字符界面中文乱码 - chinabinlang的专栏 - CSDN博客1

    Ubuntu 字符界面中文乱码解决方案 Ubuntu 字符界面中文乱码是指在 Ubuntu 系统中,字符界面无法正常显示中文字符的问题。这种问题的出现可能是因为 Ubuntu 的默认设置不支持中文显示,或者是因为系统的 locale ...

    字符编码解决方案

    例如,如果一个UTF-8编码的程序尝试将ANSI编码的文件内容转换为UNICODE,就会出现乱码。解决办法是确保文件以UTF-8编码保存,并在代码中进行相应的转换,避免混合不同编码的转换。 2. **路径的乱码**:在处理包含非...

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

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

    java 编码,乱码,字符系列(1)

    本文将围绕“Java编码与乱码”这一主题展开,探讨字符编码系列的第一部分。 首先,我们需要理解什么是字符编码。字符编码是计算机用于表示文字、符号等字符的方式,它为每个字符分配了一个唯一的数字,使得计算机...

    java字符编码监听器

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

    字符编码 自动识别 编码类型

    当一个文本文件由一种编码方式创建,但在另一种不兼容的编码下打开时,就会出现乱码。比如,一个GB2312编码的中文文档在UTF-8环境下打开,由于两者编码规则不同,计算机无法正确识别字符,导致显示异常。 为了解决...

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

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

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

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

    字符编码文档

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

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

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

    中英文字符编码查询助手.zip

    - 编辑器设置:确保文本编辑器设置为正确的字符编码,以免在保存文件时出现编码错误。 总之,"中英文字符编码查询助手"是一个实用的工具,能够帮助开发者、程序员以及对字符编码有需求的用户更好地理解和处理各种...

Global site tag (gtag.js) - Google Analytics