`
这些年
  • 浏览: 389919 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java中的乱码问题

 
阅读更多

Java中乱码问题很常见,原因也多种多样,这里做一个总结,不求全面,力求准确,如果错误欢迎指正。

 

1.文件页面编码导致的乱码。

每一个文件(java,js,jsp,html等)都有其本身的编码格式,文件中的代码在一种编码中显示正常,在另外一种编码下就会显示出乱码。
在Eclipse中,每一个工程都会有编码格式(Text file encoding), 一般默认为GBK。而一个比较好的编程习惯是新建一个项目,优先把项目的编码设为UTF-8。
这样做的原因很简单,UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。几种常见的字符集,GBK,GB2312,UTF-8之间的关系如下:
GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换

有兴趣了解更多,可以参考如下链接的文章,写的很好。

http://www.cnblogs.com/xiaomia/archive/2010/11/28/1890072.html

 

2.不同字符集的字符串转换时导致的乱码。
每一个String,底层实现都是用一个byte数组存储,使用不同的字符集,存储的数组长度当然就不同。如果不使用同一种字符集进行解码,就一定会出现乱码。
例如如下代码:

Java代码  收藏代码
  1. import java.io.UnsupportedEncodingException;  
  2. import java.nio.charset.Charset;  
  3. public class TestCharset {  
  4.   
  5.     public static void main(String[] args) throws UnsupportedEncodingException {  
  6.           
  7.         String strChineseString = "中文";  
  8.         String encoding = System.getProperty("file.encoding");  
  9.         System.out.println("系统默认的字符集是:" + encoding);  
  10.         System.out.println(strChineseString.getBytes(Charset.forName("GBK")).length);  
  11.         System.out.println(strChineseString.getBytes(Charset.forName("UTF-8")).length);  
  12.         System.out.println(strChineseString.getBytes().length);  
  13.     }  
  14. }  

 

输出结果为:

Java代码  收藏代码
  1. 系统默认的字符集是:UTF-8  
  2. 4  
  3. 6  
  4. 6  

 

可以看出,使用GBK和UTF-8编码,得到的byte数组长度不一样,原因就是utf-8使用3个字节来编码中文,而GBK使用2个字节来编码中文。因为我的项目默认使用UTF-8,所以使用不加参数的getBytes()得到的数组长度和使用UTF-8编码的 字符串长度一样。关于字符集的详细知识可以参考第一部分中给出的文章地址。

 JDK中关于getBytes方法的描述:
 getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。

 getBytes(Charset charset) 使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

每一个字符串底层都有自己的编码方式。不过一旦调用getByte方法后,得到的byte数组就是使用某种特定字符集编码后的数组,不需要再做多余的转换。

当得到上面的byte数组后,就可以调用String的另外一个方法来生成需要转码的String了。
测试例子如下:

Java代码  收藏代码
  1. import java.io.UnsupportedEncodingException;  
  2. import java.nio.charset.Charset;  
  3. public class TestCharset {  
  4.   
  5.     public static void main(String[] args) throws UnsupportedEncodingException {  
  6.         String strChineseString = "中文";  
  7.         byte[] byteGBK = null;  
  8.         byte[] byteUTF8 = null;  
  9.         byteGBK = strChineseString.getBytes(Charset.forName("GBK"));  
  10.         byteUTF8 = strChineseString.getBytes(Charset.forName("utf-8"));  
  11.         System.out.println(new String(byteGBK,"GBK"));  
  12.         System.out.println(new String(byteGBK,"utf-8"));  
  13.         System.out.println("**************************");  
  14.         System.out.println(new String(byteUTF8,"utf-8"));  
  15.         System.out.println(new String(byteUTF8,"GBK"));  
  16.     }  
  17. }  

 

输出结果为:

Java代码  收藏代码
  1. 中文  
  2. ����  
  3. **************************  
  4. 中文  
  5. 涓枃  

 

可以看出,使用哪种字符集编码一个String,在生成一个String的时候就必须使用相应的编码,否则就会出现乱码。
简单来讲,只有满足如下公式的String转码,才不会乱码。

Java代码  收藏代码
  1. String strSource = "你想要转码的字符串";  
  2. String strSomeEncoding = "utf-8";   //例如utf-8  
  3. String strTarget = new String (strSource.getBytes(Charset.forName(strSomeEncoding)), strSomeEncoding);  

 
JDK中关于getBytes方法的描述:
String(byte[] bytes)  通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。 
String(byte[] bytes, Charset charset)  通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。

 

3.Socket网络传输时导致的中文乱码。
使用Socket进行通讯的时候,传输有多种选择,可以使用PrintStream,也可以使用PrintWriter。传输英文还好,传输中文就可能出现乱码问题。网上的说法很多,经过实际测试,发现问题还在字节和字符的问题上面。

众所周知,Java中分为字节流和字符流,字符(char)是16bit的,字节(BYTE)是8bit的。PrintStrean是写入一串8bit的数据的。 PrintWriter是写入一串16bit的数据的。 
String缺省是用UNICODE编码,是16bit的。因此用PrintWriter写入的字符串,跨平台性好一些,PrintStream的可能会出现字符集乱码。

可以这样理解上面的话,PrintStream是用来操作byte, PrintWriter是用来操作Unicode, PrintStream一次读8bit的话,如果遇到汉字(一个汉字占16bit),就可能会出现乱码。一般需要处理中文时用PrintWriter好了。

最后网站测试,使用PrintWriter没有出现乱码。代码如下:

Java代码  收藏代码
  1. import java.io.BufferedReader;  
  2. import java.io.DataOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.OutputStreamWriter;  
  5. import java.io.PrintWriter;  
  6. import java.net.Socket;  
  7.   
  8. public class TestSocket {  
  9.   
  10.     public static void main(String[] args) throws IOException {  
  11.         Socket socket = new Socket();  
  12.         DataOutputStream dos = null;  
  13.         PrintWriter pw = null;        
  14.         BufferedReader in = null;  
  15.         String responseXml = "要传输的中文";  
  16.         //..........  
  17.         dos = new DataOutputStream(socket.getOutputStream());  
  18.         pw = new PrintWriter(new OutputStreamWriter(dos));  //不带自动刷新的Writer           
  19.         pw.println(responseXml);  
  20.         pw.flush();  
  21.     }  
  22. }  

 

需要注意的方面是,需要使用PrintWriter的println而不是write方法,否则服务器端会读不到数据的。原因就是println会在输出的时候在字符串后面加一个换行符,而write不会。
Println和write具体区别可以参考如下网址,里面的网友有讨论:
http://www.oschina.net/question/101123_17855

 

4.JSP中显示中文的乱码。
有的时候JSP页面在显示中文的时候会有乱码,大多数情况就是字符集配置和页面编码的问题。只要保证如下的几个配置没有问题,一般就不会有乱码出现。
a.JSP页面顶端添加如下语句:

Java代码  收藏代码
  1. <%@ page contentType="text/html; charset=utf-8" language="java" errorPage="" %>  

 

b.在HTML的head标签中添加如下语句。

Java代码  收藏代码
  1. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  

 

c.保证JSP的页面编码与上面两个的charset相同,这点我有在文章的第一点说过。

上面的字符集可以根据需要自己灵活选择,不一定非要utf-8。不过因为utf-8对各国语言,特别是中文支持较好,所以推荐使用。我就曾经遇到过滘在GB2312编码的页面无法正常显示的问题。
 
5.Post和Get传递中文,后台获取乱码。
前台传递中文也分为Get和Post方法。


a.Get方法的情况:
Get方法的时候主要是URL传递中文。
如果是在js文件中,可以使用如下代码进行中文转码。

Js代码  收藏代码
  1. var url ="http://www.baidu.com/s?industry=编码"  
  2. url = encodeURI(url);  

 

如果是在jsp文件中,则可以使用如下语句进行转码。
页面开始引入:

Java代码  收藏代码
  1. <%@ page import="java.net.URLEncoder" %>  

 

      需要转码的地方使用URLEncoder进行编码:

Js代码  收藏代码
  1. <a href="xxxxx.xx?industry=<%=URLEncoder.encode("http://www.baidu.com/s?wd=编码", "UTF-8")%>">  

 

无论使用哪种方法,在后台获取中文的时候都要使用如下代码:

Java代码  收藏代码
  1. request.setCharacterEncoding("utf-8");  
  2. String industry = new String(  
  3. request.getParameter("industry ").getBytes("ISO8859-1"),"UTF-8");    

【注】
1.对于request,是指提交内容的编码,指定后可以通过getParameter()则直接获得正确的字符串,如果不指定,则默认使用iso8859-1编码,为了统一,需要提交指定传输编码。
2.上面代码的第二句好像和第2条中给出的公式矛盾。我也纠结了好久,最后发现ISO8859-1是一种比较老的编码,通常叫做Latin-1,属于单字节编码,正好和计算机最基础的表示单位一致,因此使用它进行转码一般也没有问题。
iso-8859-1是JAVA网络传输使用的标准字符集,而gb2312是标准中文字符集,当你作出提交表单等需要网络传输的操作的时候,就需要把 iso-8859-1转换为gb2312字符集显示,否则如果按浏览器的gb2312格式来解释iso-8859-1字符集的话,由于2者不兼容,所以会是乱码。为了省事,建议统一使用utf-8字符集。
b.POST方法的情况。 
对于Post的情况就比较简单了,只需要在post的函数调用部分,制定post的header的字符集,如:

Js代码  收藏代码
  1. xmlHttp.open("post", url , true);  
  2. xmlHttp.setRequestHeader("Content-Type","text/xml; charset= utf-8");   
  3. xmlHttp.send(param);  

 

其中param为要传递的参数。

后台部分和get方法一样,设置如下即可,注意传输和接受的字符集要统一。

没懂的话可以参考如下文章,写的挺好。
http://www.cnblogs.com/qiuyi21/articles/1089555.html
 

6.后台向前台传递中文乱码。
在这里提供一个函数,通过这个函数来发送信息,就不会出现乱码,核心思想也是设置response流的字符集。函数代码如下:

Java代码  收藏代码
  1. /** 
  2.  * @Function:writeResponse 
  3.  * @Description:ajax方式返回字符串 
  4.  * @param str:json 
  5.  * @return:true:输出成功,false:输出失败 
  6.  */  
  7. public boolean writeResponse(String str){  
  8.     boolean ret = true;  
  9.     try{  
  10.         HttpServletResponse response = ServletActionContext.getResponse();  
  11.         response.setContentType("text/html;charset=utf-8");  
  12.         PrintWriter pw = response.getWriter();  
  13.         pw.print(str);  
  14.         pw.close();  
  15.     }catch (Exception e) {  
  16.         ret = false;  
  17.         e.printStackTrace();  
  18.     }  
  19.     return ret;  
  20. }     

 

7.下载文件时文件名乱码。
下过下载的人都知道下载的文件容易出现乱码,原因也是没有对输出流的编码格式进行限定。

附上一段代码,用来帮你完成无乱码下载。

Java代码  收藏代码
  1. HttpServletResponse response = ServletActionContext.getResponse();  
  2. response.setContentType("text/html;charset=utf-8");  
  3. response.reset();  
  4. String header = "attachment; filename=" + picName;  
  5.       header = new String(header.getBytes(), "UTF-8");  
  6.       response.setHeader("Content-disposition", header);  

核心代码就上几句,注意第二句和第三句的reset的顺序不能搞错。
reset的作用是用来清空buffer缓存的,清空请求前部的一些空白行。

 

以上只是做了比较简单的总结,具体乱码有的时候可能是多个情况的组合,具体问题具体分析。如果错误欢迎指正。

 

8:String(byte[] bytes, Charset charset) 和 getBytes() 使用

 

@Test
 	public void testBytes(){
 		//字节数
 		//中文:ISO:1 GBK:2 UTF-8:3   
 		//数字或字母: ISO:1 GBK:1 UTF-8:1
 		String username = "中";
 		try {
 			//得到指定编码的字节数组    字符串--->字节数组
 			byte[] u_iso=username.getBytes("ISO8859-1");
 			byte[] u_gbk=username.getBytes("GBK");
 			byte[] u_utf8=username.getBytes("utf-8");
 			System.out.println(u_iso.length);
 			System.out.println(u_gbk.length);
 			System.out.println(u_utf8.length);
 			//跟上面刚好是逆向的,字节数组---->字符串
 			String un_iso=new String(u_iso, "ISO8859-1");
 			String un_gbk=new String(u_gbk, "GBK");
 			String un_utf8=new String(u_utf8, "utf-8");
 			System.out.println(un_iso);
 			System.out.println(un_gbk);
 			System.out.println(un_utf8);		
 			//有时候必须是iso字符编码类型,那处理方式如下
 			String un_utf8_iso=new String(u_utf8, "ISO8859-1");		
 			//将iso编码的字符串进行还原
 			String un_iso_utf8=new String(un_utf8_iso.getBytes("ISO8859-1"),"UTF-8");
 			System.out.println(un_iso_utf8);				
 			
 		} catch (UnsupportedEncodingException e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();
 		}
 	}

 测试结果:

 

1
 2
 3
 ?
 中
 中
 ä¸­
 中

 

乱码原因:为什么使用ISO8859-1编码再组合之后,无法还原"中"字呢,其实原因很简单,因为ISO8859-1编码的编码表中,根本就没有包含汉字字符,当然也就无法通过"中".getBytes("ISO8859-1");来得到正确的"中"字在ISO8859-1中的编码值了,所以再通过new String()来还原就无从谈起了.

有时候,为了让中文字符适应某些特殊要求(如http header头要求其内容必须为iso8859-1编码),可能会通过将中文字符按照字节方式来编码的情况,如:
String s_iso88591 = new String("中".getBytes("UTF-8"),"ISO8859-1"),这样得到的s_iso8859-1字符串实际是三个在ISO8859-1中的字符,在将这些字符传递到目的地后,目的地程序再通过相反的方式String s_utf8 = new String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")来得到正确的中文汉字"中".这样就既保证了遵守协议规定、也支持中文.

分享到:
评论

相关推荐

    安装NumPy教程-详细版

    附件是安装NumPy教程_详细版,文件绿色安全,请大家放心下载,仅供交流学习使用,无任何商业目的!

    语音端点检测及其在Matlab中的实现.zip

    语音端点检测及其在Matlab中的实现.zip

    C#文档打印程序Demo

    使用C#完成一般文档的打印,带有页眉,页脚文档打印,表格打印,打印预览等

    DirectX修复工具-4-194985.zip

    directx修复工具 DirectX修复工具(DirectX repair)是系统DirectX组件修复工具,DirectX修复工具主要是用于检测当前系统的DirectX状态,若发现异常情况就可以马上进行修复,非常快捷,使用效果也非常好。

    Python手动实现人脸识别算法

    人脸识别的主要算法 其核心算法是 欧式距离算法使用该算法计算两张脸的面部特征差异,一般在0.6 以下都可以被认为是同一张脸 人脸识别的主要步骤 1 获得人脸图片 2 将人脸图片转为128D的矩阵(这个也就是人脸特征的一种数字化表现) 3 保存人脸128D的特征到文件中 4 获取其他人脸转为128D特征通过欧式距离算法与我们保存的特征对比,如果差距在0.6以下就说明两张脸差距比较小

    全国大学生信息安全竞赛知识问答-CISCN 题库.zip

    ciscn 全国大学生信息安全竞赛知识问答-CISCN 题库.zip

    JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译).zip

    JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)

    strcmp函数应用.zip

    strcmp函数应用.zip

    蓝桥杯单片机第十一届国赛设计题试做

    蓝桥杯单片机第十一届国赛设计题试做

    基于MATLAB的pca人脸识别.zip

    基于MATLAB的pca人脸识别.zip

    520.html

    520.html

    JAVA在线考试管理系统(源代码+LW+开题报告+外文翻译+英文文献+答辩PPT).zip

    JAVA在线考试管理系统(源代码+LW+开题报告+外文翻译+英文文献+答辩PPT)

    STR710的定时器编程C语言例子,开发环境为IAR EWARM。.zip

    STR710的定时器编程C语言例子,开发环境为IAR EWARM。.zip

    基于物品的协同过滤推荐算法(Python).zip

    协同过滤算法(Collaborative Filtering)是一种经典的推荐算法,其基本原理是“协同大家的反馈、评价和意见,一起对海量的信息进行过滤,从中筛选出用户可能感兴趣的信息”。它主要依赖于用户和物品之间的行为关系进行推荐。 协同过滤算法主要分为两类: 基于物品的协同过滤算法:给用户推荐与他之前喜欢的物品相似的物品。 基于用户的协同过滤算法:给用户推荐与他兴趣相似的用户喜欢的物品。 协同过滤算法的优点包括: 无需事先对商品或用户进行分类或标注,适用于各种类型的数据。 算法简单易懂,容易实现和部署。 推荐结果准确性较高,能够为用户提供个性化的推荐服务。 然而,协同过滤算法也存在一些缺点: 对数据量和数据质量要求较高,需要大量的历史数据和较高的数据质量。 容易受到“冷启动”问题的影响,即对新用户或新商品的推荐效果较差。 存在“同质化”问题,即推荐结果容易出现重复或相似的情况。 协同过滤算法在多个场景中有广泛的应用,如电商推荐系统、社交网络推荐和视频推荐系统等。在这些场景中,协同过滤算法可以根据用户的历史行为数据,推荐与用户兴趣相似的商品、用户或内容,从而提高用户的购买转化率、活跃度和社交体验。 未来,协同过滤算法的发展方向可能是结合其他推荐算法形成混合推荐系统,以充分发挥各算法的优势。

    JAVA文件传输(lw+源代码).zip

    FTP(File Transfer Protocol)是文件传输协议的简称。 FTP的主要作用,就是让用户连接上一个远程计算机(这些计算机上运行着FTP服务器程序)查看远程计算机有哪些文件,然后把文件从远程计算机上拷到本地计算机,或把本地计算机的文件送到远程计算机去。 目前FTP服务器软件都为国外作品,例如Server_U、IIS,国内成熟的FTP服务器软件很少,有一些如(Crob FTP Server),但从功能上看来远不能和那些流行的服务器软件媲美。

    python项目源码-深度学习tensorflow的滚动轴承故障诊断方法源码(高分大作业).rar

    本项目基于深度学习TensorFlow框架,针对滚动轴承故障诊断方法进行研究。项目采用了卷积神经网络(CNN)对轴承振动信号进行特征提取和分类,实现了对滚动轴承不同故障类型的自动诊断。 在技术实现上,项目利用TensorFlow搭建了一个高效的CNN模型,通过多层卷积、池化操作以及全连接层,自动学习轴承振动信号中的故障特征。同时,采用交叉熵损失函数优化模型参数,提高故障识别率。此外,项目还集成了数据预处理、模型训练、测试评估等功能模块,方便用户快速上手并进行实验研究。 经过运行测试,该项目代码运行稳定,诊断效果良好,可广泛应用于滚动轴承故障诊断领域。对于计算机相关专业的在校学生、老师或企业员工来说,该项目是一份难得的高分大作业资源,同时也是小白学习和实际项目借鉴的优秀参考资料。请放心下载使用,为您的学习和工作提供帮助!

    超详细的SpringBoot框架入门教程 Spring Boot框架快速入门教程以大量示例讲解了Spring Boot在各类情境

    超详细的SpringBoot框架入门教程 Spring Boot框架快速入门教程以大量示例讲解了Spring Boot在各类情境中的应用,让大家可以跟着老师的思维和代码快速理解并掌握。适用于Java 开发人员,尤其是初学Spring Boot的人员和需要从传统 Spring 转向 Spring Boot 开发的技术人员。 下边是动力节点的SpringBoot教程非常适合初学入门,讲的非常详细,而且全程无废话!

    毕业设计[主机域名]ISPConfig 3.0.1.3_ispconfig3-codepub.zip

    毕业设计[主机域名]ISPConfig 3.0.1.3_ispconfig3-codepub.zip

    matlab开发-用交叉熵优化多变量宏观模型随机多极值优化.zip

    matlab开发-用交叉熵优化多变量宏观模型随机多极值优化.zip

    矩阵特征值的计算方法.zip

    矩阵特征值的计算方法.zip

Global site tag (gtag.js) - Google Analytics