JAVA WEB 乱码问题解析

乱码原因

  在Java Web开发过程中,经常遇到乱码的问题,造成乱码的原因,概括起来就是对字符编码和解码的方式不匹配。

  既然乱码的原因是字符编码与解码的方式不匹配,那么为什么我们一定要对字符进行编码,不编码可不可以呢?这是因为在计算机中存储数据的基本单位是1个字节,即8个bit,那么它所能表达的字符的最多有28=256个,而在我们现实社会中存在的字符(汉字、英文、其他文字等等)远远多余这个数字,所以为了解决字符与字节的矛盾,对字符进行编码处理才能存储在计算机中。

编码与解码

  在计算机中常见的编码方式有ASCII、ISO-8859-1、GB2312、UTF-16、UTF-8几种编码方式。

  ASCII码是使用一个字节的低7位来表示的,所以共能表达的字符最多有27=128个。ISO-8859-1是ISO组织基于ASCII码的基础上扩展来的,兼容ASCII码,涵盖了大多数西欧字符。ISO8859-1使用一个字节来表示,所以其能表达的字符最多有256个。GB2312,采用了双字节编码,编码范围是A1-F7,其中A1-A9是符号区,B0-F7是汉字区,包含6763个汉字。GBK是为了扩展GB2312编码,并加入了更多的汉字,总能表达的汉字有21003个。UTF-16是采用定长的编码方式,无论什么字符都采用2个字节进行表示,这也是JAVA内存中字符的存储格式。与UTF-16相反,UTF-8采用了变长的编码方式,不同的类型的字符可以由1-6个字节组成。

  下面以字符串“日向雏田”来看一下在计算机中不同编码方式的编码,如下图。

乱码分析与解决

  对于JAVA WEB中乱码问题,我们划分位请求导致的乱码和响应导致的乱码,对于不同的乱码我们要分析其乱码原因,即字符编码的方式是什么,解码的方式是什么。

  对于由于请求导致的乱码我们要分析Http请求,查看其编码方式,由于HTTP请求分为Get请求和Post请求,我们接下来分别对其进行讨论。

  对于Get请求,是浏览器默认的请求方式,和表单提交时设置为“Get”时的提交方式。我们通过火狐浏览器我们查看其具体内容如下:

  地址栏为:

  请求内容为:

  

  通过上面请求我们可以看到,GET请求中查询字符串放在了请求行中存放,发送到WEB服务器中,通过“日向雏田”编码我们可以看到,浏览器对该字符串采用的编码方式为“UTF-8”。

  查看服务器代码我们可以看到乱码(如下图),这是因为服务器在接受到该字符串编码后的数据默认通过ISO-8859-1的方式进行解码,所以造成了编码与解码的方式不统一。

  

  解决方案如下:

  首先获取字符串user解码前的编码,然后指定该字符串的编码方式,如下图:

  解决方案示意图如下:

  在Java web开发过程中,我们在超链接中传递参数,经常遇到中文的情况。对此情况下,我们需要对中文进行编码,我们可以设置为UTF-8,解码方案同上。

  

<a href="${pageContext.request.contextPath}/Test?user=<%=URLEncoder.encode("日向雏田", "UTF-8")%>">点击</a>

  对于Post请求,是表单提交时设置为“Post”时的提交方式。我们通过火狐浏览器我们查看其具体内容如下:

  地址栏及其页面为:

  

  post请求内容为:

  

  由上图我们可以知道,在post请求中,将请求内容直接放在请求体中发送给web服务器,编码方式为“utf-8”。

  在此响应Servlet中,doPost方法体如下:

  

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String user=request.getParameter("user");
System.out.println(user);//输出为日向雏田
}

  此处乱码的原因依然时在代码getParameter(“user”)时,web服务器采用默认的解码方案“ISO-8859-1”进行解码,导致了编码与解码方案的不同意,解决方案可以采用get请求乱码的解决方案,但是还有一种更为简单的解决方案,直接指定方法体的编码/解码方案为“utf-8”。方案如下。

  

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");  //设置请求体的编码/解码方案为UTF-8 但是请求行的编码解码方案不会受影响
String user=request.getParameter("user");
System.out.println(user);          //输出为日向雏田
}

  以上对于请求导致的乱码情况分析完毕。

  在影响导致的乱码中,web服务器会将响应的内容写入响应体中,返回给客户端并不会涉及到状态行中的情况。如向浏览器输出”HelloWorld“其响应如下图所说。

  对于响应导致的乱码我们不得不涉及到四个方法,如下:

 

response.setHeader("Content-Type", "text/html;cahrset=utf-8");//设置发送到客户端的响应的内容类型和响应内容的编码类型(响应体的编码类型)
response.setCharacterEncoding("utf-8");//设置响应体的编码类型
response.getWriter();           //获取响应的输出字符流 
response.getOutputStream();        //获取响应的输出字节流

  对于设置响应体的编码类型,如response.setHeader("Content-Type", "text/html;cahrset=utf-8");与response.setCharacterEncoding("utf-8");这2个方法设置的编码方式等效,若没有设置响应体的编码方式,则默认为ISO-8859-1,而且后面设置响应体字符的编码方式会迭代前面的设置编码的方式。这两个方法均在getWriter方法前有效,在getWriter方法设置编码的方法会无效。

  但是这2个方法却有点不同,即setHeader("Content-Type", "text/html;cahrset=utf-8")这个方法浏览器会自动采用该响应体的编码方式进行解码,而setCharacterEncoding()该方法并不是所有的浏览器都会采用该方法的编码方式进行解码,下面对这2个方法进行测试,效果如下:

	public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("Content-Type", "text/html;charset=utf-8");
response.getWriter().write("日向雏田");
}

  

  

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
response.getWriter().write("日向雏田");
}

  

  从上面可以看到第一个方法对于浏览器来说,支持的较好,提倡采用第一种方法设置响应体的字符编码方式。

  对于获取响应字符输出流的方法,如果在此之前没有设置响应体的编码方式,那么默认为null,即ISO-8859-1方式进行编码。而且后面设置的编码方式会覆盖前面设置的编码方式。在getWriter()方法之后设置的编码无效。

  对于获取响应输出字节流,我们在输出字符串时,我们需要设置字符串的编码方式如果没有那么默认ISO-8859-1。

  对于前面2个输出流,由于只有一个输出缓存,所以这两个方法互斥。

  以上,为了保证响应无乱码,需要保证字符编码和解码方法的统一,方案如下:

	public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 方案1
// response.setHeader("Content-Type", "text/html;charset=utf-8");
// response.getWriter().write("日向雏田");
// 方案2
// response.getOutputStream().write("日向雏田".getBytes("UTF-8"));
// 方案1,2互斥
}

  

  此外在Java web开发过程中,我们还会遇到当进行文件下载时,中文文件名导致的问题,如下图所示:

	public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String realPath=this.getServletContext().getRealPath("/src/日向雏田.jpg");
String fileName=realPath.substring(realPath.lastIndexOf('\\')+1);
response.setHeader("content-disposition", "attachment;filename="+fileName);
InputStream is=new FileInputStream(new File(realPath));
OutputStream os=response.getOutputStream();
byte[] buff=new byte[1024];
int len=0;
while((len=is.read(buff))>0){
os.write(buff, 0, len);
}
os.close();
is.close();
}

  采用火狐浏览器进行测试,查看页面效果,及其响应结果如下:

  

  经过查看响应头分析,下载文件名存放在响应头中,且对于中文文字没有采用UTF-8、UTF-16、GBK等等能识别中文的编码,那么对于中文文件名导致采用哪种编码方式呢?查看REF 7578得知,在此处采用ASCII编码,但是REF规定,如果不可避免的要使用非ASCII码的字符,程序员应该均匀的使用UTF-8,来最小化交互操作的问题。

  所以,解决方案就是把文件名编码成UTF-8,传递给响应头,浏览器(部分)默认对该文件名进行UTF-8解码处理。

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String realPath=this.getServletContext().getRealPath("/src/日向雏田.jpg");
String fileName=realPath.substring(realPath.lastIndexOf('\\')+1);
String utf_8Name=URLEncoder.encode(fileName,"utf-8");//解决方案
response.setHeader("content-disposition", "attachment;filename="+utf_8Name);
InputStream is=new FileInputStream(new File(realPath));
OutputStream os=response.getOutputStream();
byte[] buff=new byte[1024];
int len=0;
while((len=is.read(buff))>0){
os.write(buff, 0, len);
}
os.close();
is.close();
}

  效果如下:其中火狐浏览器并没有对其解码

  

文章最后欣赏老婆的美照:

    

  

  

1.JAVA WEB 笔记中文乱码的更多相关文章

  1. 第三章 深入分析Java Web的中文乱码问题(待续)

    几种常见的编码格式 在Java中需要编码的场景 在Java中如何编解码 在Java Web中涉及的编解码 在JS中的编码问题 常见问题分析 一种繁简转换的实现方式

  2. Java编程中中文乱码问题的研究及解决方案

    0 引言 Java最大的特性是与平台的无关性及开发环境的多样性.字符串被Java应用程序转化之前,是根据操作系统默认的编码方式编码.Java语言内部采用Unicode编码,它是定长双字节编码,即任何符 ...

  3. JavaWeb使用Filter进行字符编码过滤 预防web服务中文乱码

    JavaWeb使用Filter进行字符编码过滤 预防web服务中文乱码 准备条件:一个创建好的 JavaWeb 项目 步骤: 1.创建一个类并实现 Filter 接口 import javax.ser ...

  4. java socket输入输出中文乱码问题

    http://hi.baidu.com/linjk03/item/e2028bfd990c14ea1a111feb 统一了输入输出的编码格式,是不会有乱码问题出现的.   构造Reader或Write ...

  5. java: jsp:param中文乱码

    java: jsp:param中文乱码 假如a.jsp/b.jsp文件中 a.jsp代码: 需要加入:request.setCharacterEncoding("UTF-8")  ...

  6. Java Web中解决乱码的方式

    Java Web中解决乱码的方式 方式一:添加编码过滤器 package com.itmacy.dev.filter; import javax.servlet.*; import javax.ser ...

  7. Java Web笔记(2)

    学习笔记,狂神说java,链接:https://www.bilibili.com/video/av68833391 5.Maven 我为什么要学习这个技术? 在Javaweb开发中,需要使用大量的ja ...

  8. java开发中中文乱码总结

    1.jsp页面内容显示乱码 这种乱码原因很简单,一般的工具或解码程序对中文字符解析时采用默认的解码方式: <%@ page contentType="text/html; charse ...

  9. Java WEB 笔记

    1. 部署并启动 tomcat 服务器 1). 解压 apache-tomcat-version 到一个非中文目录下 2). 配置一个环境变量,JAVA_HOME(指向 JDK 安装目录)或 JRE_ ...

随机推荐

  1. 使用Java语言开发微信公众平台(六)

    在上一节课程中,我们来学习了微信公众平台最基础的一个接口--access_token,并且能够从微信公众平台中取到access_token. 那么,在本节课程中,我们要以上节课获取到的access_t ...

  2. Mongo汇总问题

    1. 数据 /* 5 */ { "_id" : ObjectId("5902f7ca2b3fe442d60a0946"), "code" : ...

  3. 需求收集实例三之 FM

    暂且叫这个项目叫FM.FM项目采用敏捷模式,需求的表现形式是Story. 此项目需求收集过程如下: 亮点:在公司第一次实践敏捷.用Story 而非 需求说明文档呈现需求. 败笔:没有处理好Story ...

  4. [Splay模版1]

    输入 第1行:1个正整数n,表示操作数量,100≤n≤200,000 第2..n+1行:可能包含下面3种规则: 1个字母'I',紧接着1个数字k,表示插入一个数字k到树中,1≤k≤1,000,000, ...

  5. 【算法系列学习】Dijkstra求最短路 [kuangbin带你飞]专题四 最短路练习 D - Silver Cow Party

    https://vjudge.net/contest/66569#problem/D trick:1~N各点到X可以通过转置变为X到1~N各点 #include<iostream> #in ...

  6. css浮动布局,浮动原理,清除(闭合)浮动方法

    css浮动 1.什么是浮动:在我们布局的时用到的一种技术,能够方便我们进行布局,通过让元素浮动,我们可以使元素在水平上左右移动,再通过margin属性调整位置 2.浮动的原理:使当前元素脱离普通流,相 ...

  7. php表单提交 图片、音乐、视频、文字,四种类型共同提交到数据库

    这个问题一直困扰了我好几天,终于在今天让我给解决了,难以掩饰的激动. 其实在之前没有接触到这种问题,只是表单提交数据而已,再就是图片,四种类型同时提交还真是没遇到过,做了一个系统,其中有一个功能就是提 ...

  8. 关于使用JavaScript实现图片点击切换(附带改变导航图片 方案二)

    具体需求见方案一. 这种方案相比方案一更加专业. <!DOCTYPE html> <html> <head lang="en"> <met ...

  9. hdu1054 Strategic Game 树形DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054 思路:树形DP,用二分匹配也能解决 定义dp[root][1],表示以root 为根结点的子树且 ...

  10. nrm是什么?以及nrm的安装与命令

    nrm的作用与安装使用 一.nrm是什么? 这是官方的原话: 开发的npm registry 管理工具 nrm, 能够查看和切换当前使用的registry, 最近NPM经常 down 掉, 这个还是很 ...