整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Java Web开发中文乱码总结-先收藏了慢慢看

开发过程中,经常遇到中文乱码问题,以前总是解决就好,并没有对该问题总结一下,现在来总结一下开发过程中常见的中文乱码问题。

一、有必要了解一些基本的编码知识:

  • 这篇字符编码笔记是必读的

  • jsp的三次编码

    第一阶段:JVM将.jsp文件编译为.java文件。JVM先读取pageEncoding的值,根据该值去读取.jsp文件,然后由指定的编码方案生成UTF-8的.java文件。

    第二阶段:JVM将.java文件转换为.class文件,从UTF-8至UTF-8。这个过程就与任何编码的设置都没有关系了,经过这个阶段后.java文件就转换成了统一的UTF-8编码的.class文件了。

    第三阶段:服务器将处理的结果返回给浏览器,这个阶段则依靠contentType的charset,如果设置了charset则浏览器就会使用指定的编码格式进行解码,否则采用默认的ISO-8859-1编码格式进行解码处理。

  • jsp中的编码设置

  1. pageEncoding:<%@ page pageEncoding=”UTF-8”%>

    上文中第一阶段,使用该值去读取jsp文件,为避免中文乱码,跟jsp文件编码一致;对服务器响应进行重新编码,即jsp的输出流在浏览器中显示的编码(不是主要作用)。

  2. contentType: <%@ page contentType=”text/html;charset=UTF-8”%>

    使用该值对服务器响应进行重新编码,即jsp的输出流在浏览器中显示的编码;对表单get和post请求数据编码;上文中第一阶段,使用该值去读取jsp文件(不是主要作用)。

  3. < META http-equiv=”Content-Type” content=”text/html;charset=UTF-8”>

    网页的编码信息 ,说明页面制作所使用的编码。

  4. request.setCharacterEncoding()

    可用在servlet和jsp页面中,作用是设置对客户端请求进行重新编码的编码,即post方式提交的数据进行编码。

  5. response.setCharacterEncoding()

    与<%@ page contentType=”text/html;charset=UTF-8”%>一样。

  6. response.setContentType()

    与<%@ page contentType=”text/html;charset=UTF-8”%>一样。

  7. response.setHeader(“Content-Type”,”text/html;charset=UTF-8”)

    与< META http-equiv=”Content-Type” content=”text/html; charset=UTF-8”>一样。

    注意:上文1,2,3中有部分功能是一样的,是有优先级的,在读取jsp文件时,1>2;在对服务器响应进行编码的时候,2>1>3,一般情况下,1,2都写。

  • http请求默认以”ISO-8859-1”的编码来传送URL的。

  • 二、中文乱码的几种情况及最简单的解决方案:

    • pageEncoding设置错误

      pageEncoding设置为jsp文件的编码类型。

    • 查询字符串包含中文

      中文的编码方式取决于浏览器,chrome为UTF-8,IE为GB2312,这是由于浏览器并没有遵循URI编码规范。有两种解决方法:

    1. 开发过程中,将查询字符串提前编码,

      如:http://www.baidu.com/demo?demo=%D6%D0%B9%FA (UTF-8编码)

    2. 在Servlet的doGet()方法中添加

    1
    String value = new String(request.getParameter("parameterName").getBytes("ISO-8859-1"),"浏览器的编码方式");
    • 表单中的get和post数据包含中文

      中文的编码方式取决于上文的contentType中的charset,有两种解决办法:

    1. 在Servlet的doPost()方法中添加request.setCharacterEncoding(“charset的值”);(仅对post有用)

    2. 在Servlet的doPost()方法中添加

    1
    String value = new String(request.getParameter("parameterName").getBytes("ISO-8859-1"),"charset的值");

    三、原理

    我们通过上面的方法可以解决乱码问题,下面讲讲原理:

    • 客户端发到服务器的数据需要在客户端进行编码,类似于:String parameterName = "中国".getBytes("UTF-8")然后将编码后的数据发到服务器。

    • 客户端接受数据,request.getParameter(“”)的作用就是对接收到的数据进行解码,默认使用ISO-8859-1进行解码,可以使用request.setCharacterEncoding(“”)进行设置,但仅对post有用。假如我们使用默认的ISO-8859-1,肯定乱码,因为编码跟解码不一致,那此时怎么办呢,引出了上文中的两种解决方案:使用request.setCharacterEncoding(“”)改变request.getParameter(“”)的解码方式或者new String(request.getParameter("parameterName").getBytes("ISO-8859-1"),"charset的值")将request.getParameter(“”)解码的数据重新编码再解码。

    四、其他

    在jsp中的页面使用response.setContentType()等设置字符集会破坏jsp容器自身的页面编码,会引起html中字符乱码,脚本不会乱,所以不建议设置。在开发中多采用page指令设置字符集。

    1234567891011121314151617
    <%	response.setContentType("text/html;charset=UTF-8");	String str = new String("你好".getBytes("iso-8859-1"),"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=UTF-8"><title>Insert title here</title></head><body><p>你好</p><br><%=str %></body></html>

    网页测试

    开发一个完整的web项目时,总是会遇到各种各样的中文乱码问题,例如页面显示乱码,表单提交乱码,数据库存储乱码等等,虽然目前也能找到各种各样的解决方案,但是大部分都没有总结全面。这里我就根据自己以前的总结以及查找的一些资料进行整理,写出这篇文章。

    (1)准备知识

    凡是会出现乱码的地方都是由于编码的问题产生的,页面默认编码为ISO-8859-1,简体中文编码为GB2312,中文汉字集(简体与繁体)编码为GBK,国际编码为UTF-8。由于UTF-8编码支持的语言类型最广,所以在这里建议凡是用到字符编码的地方都使用UTF-8格式。接下来看看所有可能出现乱码场景的解决方案。

    (2)页面显示乱码

    • html页面

    在页面的<head>标签内添加<meta>标签,内容如下

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    或者

    <meta charset="utf-8">

    • jsp页面

    在jsp页面的顶部加上以下代码,重点是charset=UTF-8和pageEncoding="UTF-8"

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

    同时在<head>标签下添加<meta>标签,代码如下

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    为了让所有新建的jsp页面默认为utf-8编码,可以在相应的IDE下设置。以eclipse为例,window > Prefrences > Web > JSP Files > Encoding处改成支持UTF-8格式的选项。

    jsp页面编码设置

    (3)客户端和服务器端传输乱码

    • tomcat配置

    在tomcat安装目录下 > conf > server.xml,在server.xml文件中找到Connector port="8080"所在的标签,在标签内部添加URIEncoding="utf-8",添加后为

    <Connector port="8080"

    protocol="HTTP/1.1"

    maxThreads="150"

    connectionTimeout="200000"

    redirecPort="8443"

    URIEncoding="utf-8"/>

    • Request请求

    例如以下请求方式

    <a href="/myProject/displayServlet?username=张三&password=123">显示用户名和密码</a>

    这种情况下,可以看出来该请求是GET请求,在接收请求时如果出现乱码,需要使用以下代码

    username= new String(username.getBytes("ISO-8859-1"),"UTF-8")

    在表单提交的时候,如果是POST请求,与GET请求不一样,添加的是以下代码

    request.setCharacterEncoding("utf-8")

    注意:request.setCharacterEncoding("utf-8")只在POST请求下生效

    • Response响应

    在servlet中利用response进行输出时,如果出现乱码,需要添加以下代码

    response.setContentType("text/html;charset=utf-8");

    response.setCharacterEncoding("UTF-8")

    • struts.xml

    如果使用了Struts2框架,在strust.xml文件中添加如下代码

    <constant name="struts.i18n.encoding" value="UTF-8"></constant>

    • Filter

    还有一种最保险的方法是添加自定义编码过滤器,或者使用spring自带的过滤器,在web.xml中添加以下代码

    <filter>

    <filter-name>encodingFilter</filter-name>

    <filter-class>

    org.springframework.web.filter.CharacterEncodingFilter

    </filter-class>

    <init-param>

    <param-name>encoding</param-name>

    <param-value>UTF-8</param-value>

    </init-param>

    </filter>

    (4)数据库插入乱码

    在服务器获取到正确数据后,插入到数据库却出现乱码,很有可能是数据库的编码问题,按照以下方法来做可以避免这种问题

    • 安装

    在数据库安装过程中会选择编码方式,此时选择utf-8格式

    • 创建数据库

    在创建数据库的过程中,按照以下例子

    CREATE DATABASE `share` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

    • 创建表

    在创建表生成的SQL后面加上

    ENGINE=InnoDB DEFAULT CHARSET=utf8;

    • 数据库连接

    在使用hibernate或者spring连接数据库时,按照以下例子

    <property name="connection.url">

    <![CDATA[jdbc:mysql://localhost:3306/testDB?useUnicode=true&characterEncoding=utf8]]>

    </property>

    (5)总结

    在完成上述的讲解后,基本不会出现乱码的情形,大家也可以尝试下。如果出现了乱码的情况,也不要着急,先冷静分析,乱码是出现在哪一个环节,然后按照我讲的几部分去找对应的解决办法。

    同时也是教大家一种问问题的方式,先自己分析问题出在哪里,直接问别人“我的项目中出现了中文乱码,你能帮我看一下么?”和“数据返回页面的时候出现乱码了,你能帮我看一下么?”你觉得别人会喜欢哪种提问方式呢?

    者 | 丁彦军

    责编 | 仲培艺

    近日,有位粉丝向我请教,在爬取某网站时,网页的源代码出现了中文乱码问题,本文就将与大家一起总结下关于网络爬虫的乱码处理。注意,这里不仅是中文乱码,还包括一些如日文、韩文 、俄文、藏文之类的乱码处理,因为他们的解决方式是一致的,故在此统一说明。

    乱码问题的出现

    就以爬取 51job 网站举例,讲讲为何会出现“乱码”问题,如何解决它以及其背后的机制。

    代码示例:

    import requests
    url = "http://search.51job.com"
    res = requests.get(url)
    print(res.text)
    

    显示结果:

    打印 res.text 时,发现了什么?中文乱码!!!不过发现,网页的字符集类型采用的是 GBK 编码格式。

    我们知道 Requests 会基于 HTTP 头部对响应的编码作出有根据的推测。当你访问 r.text 之时,Requests 会使用其推测的文本编码。你可以找出 Requests 使用了什么编码,并且能够使用 r.encoding 属性来改变它。

    接下来,我们一起通过 Resquests 的一些用法,来看看 Requests 会基于 HTTP 头部对响应的编码方式。

    print(res.encoding) #查看网页返回的字符集类型
    print(res.apparent_encoding) #自动判断字符集类型
    

    输出结果为:

    可以发现 Requests 推测的文本编码(也就是网页返回即爬取下来后的编码转换)与源网页编码不一致,由此可知其正是导致乱码原因。

    乱码背后的奥秘

    当源网页编码和爬取下来后的编码转换不一致时,如源网页为 GBK 编码的字节流,而我们抓取下后程序直接使用 UTF-8 进行编码并输出到存储文件中,这必然会引起乱码,即当源网页编码和抓取下来后程序直接使用处理编码一致时,则不会出现乱码,此时再进行统一的字符编码也就不会出现乱码了。最终爬取的所有网页无论何种编码格式,都转化为 UTF-8 格式进行存储。

    注意:区分源网编码 A-GBK、程序直接使用的编码 B-ISO-8859-1、统一转换字符的编码 C-UTF-8。

    在此,我们拓展讲讲 Unicode、ISO-8859-1、GBK2312、GBK、UTF-8 等之间的区别联系,大概如下:

    最早的编码是 ISO8859-1,和 ASCII 编码相似。但为了方便表示各种各样的语言,逐渐出现了很多标准编码。ISO8859-1 属于单字节编码,最多能表示的字符范围是 0-255,应用于英文系列。很明显,ISO8859-1 编码表示的字符范围很窄,无法表示中文字符。

    1981 年中国人民通过对 ASCII 编码的中文扩充改造,产生了 GB2312 编码,可以表示 6000 多个常用汉字。但汉字实在是太多了,包括繁体和各种字符,于是产生了 GBK 编码,它包括了 GB2312 中的编码,同时扩充了很多。中国又是个多民族国家,各个民族几乎都有自己独立的语言系统,为了表示那些字符,继续把 GBK 编码扩充为 GB18030 编码。每个国家都像中国一样,把自己的语言编码,于是出现了各种各样的编码,如果你不安装相应的编码,就无法解释相应编码想表达的内容。终于,有个叫 ISO 的组织看不下去了。他们一起创造了一种编码 Unicode,这种编码非常大,大到可以容纳世界上任何一个文字和标志。所以只要电脑上有 Unicode 这种编码系统,无论是全球哪种文字,只需要保存文件的时候,保存成 Unicode 编码就可以被其他电脑正常解释。Unicode 在网络传输中,出现了两个标准 UTF-8 和 UTF-16,分别每次传输 8 个位和 16 个位。于是就会有人产生疑问,UTF-8 既然能保存那么多文字、符号,为什么国内还有这么多使用 GBK 等编码的人?因为 UTF-8 等编码体积比较大,占电脑空间比较多,如果面向的使用人群绝大部分都是中国人,用 GBK 等编码也可以。

    也可以这样来理解:字符串是由字符构成,字符在计算机硬件中通过二进制形式存储,这种二进制形式就是编码。如果直接使用 “字符串↔️字符↔️二进制表示(编码)” ,会增加不同类型编码之间转换的复杂性。所以引入了一个抽象层,“字符串↔️字符↔️与存储无关的表示↔️二进制表示(编码)” ,这样,可以用一种与存储无关的形式表示字符,不同的编码之间转换时可以先转换到这个抽象层,然后再转换为其他编码形式。在这里,Unicode 就是 “与存储无关的表示”,UTF-8 就是 “二进制表示”。

    乱码的解决方法

    根据原因来找解决方法,就非常简单了。

    方法一:直接指定 res.encoding

    import requests
    url = "http://search.51job.com"
    res = requests.get(url)
    res.encoding = "gbk"
    html = res.text
    print(html)
    

    方法二:通过 res.apparent_encoding 属性指定

    import requests
    url = "http://search.51job.com"
    res = requests.get(url)
    res.encoding = res.apparent_encoding
    html = res.text
    print(html)
    

    方法三:通过编码、解码的方式

    import requests
    url = "http://search.51job.com"
    res = requests.get(url)
    html = res.text.encode('iso-8859-1').decode('gbk')
    print(html)
    

    输出结果:

    基本思路三步走:确定源网页的编码 A---GBK、程序通过编码 B---ISO-8859-1 对源网页数据还原、统一转换字符的编码 C-UTF-8。至于为啥出现统一转码这一步呢? 网络爬虫系统数据来源很多,不可能使用数据时,再转化为其原始的数据,这样做是很废事的。所以一般的爬虫系统都要对抓取下来的结果进行统一编码,从而在使用时做到一致对外,方便使用。

    比如如果我们想讲网页数据保存下来,则会将起转为 UTF-8,代码如下:

    with open("a.txt",'w',encoding='utf-8') as f:
     f.write(html)
    

    总结

    关于网络爬虫乱码问题,这里不仅给出了一个解决方案,还深入到其中的原理,由此问题引申出很多有意思的问题,如 UTF-8、GBK、GB2312 的编码方式怎样的?为什么这样转化就可以解决问题?

    最后,多动脑,多思考,多总结,致每一位码农!

    本文为作者投稿,版权归其所有。