中文乱码问题
程序员文章站
2022-07-13 11:35:17
...
1、字符编码
在计算机中任何数据都是以二进制存储的,要存储一个字符就要对它进行编码,用一个二进制数与这对应,这种对应的规则,就是字符的编码。编码的规则有很多种,一种规则所编码的“字符”的集合就叫做“字符集”。在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的,因此,平时我们所说的“字符集”,例如GB2312、GBK和JIS等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。
最早出现的编码是ASCII码,因为早期计算机系统只支持英语。后来每个国家(或区域)规定了计算机信息交换用的字符编码集,例如中国的GB2312等作为自己国家/区域内信息处理的基础。在程序读取字符到输出字符的过程中,就需要在不同的字符集之间进行转换,这个时候就容易出现乱码,因此要了解乱码是如何产生的,首先要了解各种字符编码。下面对这些编码做一个简单介绍。
(1)、ASCII
ASCII码是《美国标准信息交换码》,简称ASCII,它总共规定了128个字符号所对应的数字代码,使用了7位二进制的位来表示这些数字。其中包含了英文的大小写字母、数字和标点符号常用的字符,数字代号从0~127。
(2)、ISO8859
ASCII码解决了英语国家的字符问题,可是欧洲各个国家的字符问题还没有解决,例如法语中就有许多英语中没有的字符,为了解决该问题,国际标准化组织的ISO8859标准应运而生,在ISO8859的编码表中,编号0~127与ASCII保持兼容,编号128~159共32个编码保留给扩充定义的32个扩充控制码,160为空格,161~255的95个数字用于新增加的字符代码。由于在一张码表中只能增加95种字符的代码,因此ISO8859实际上不是一张码表,而是一系列标准,包括14个字符码表。例如,西欧的常用字符就包含在ISO8859-1字符表中,在ISO8859-7中则包含了ASCII和现代希腊语字符。
(3)、GB2312和GBK
GB2312是中国国家标准汉字信息交换用编码,简称国标码,标准号为GB2312-80。
中国的文字不是拼音文字,汉字的个数的数万之多,远远超过区区256个字符,ISO8859无能为力,但是通过借鉴ISO8859的编码思想,研究人员解决了中文的编码问题。GB2312使用两个字节来表示一个中文,在每个字符的256种可能中,为了与ASCII保持兼容,低于128的我们不使用。借鉴ISO8859的设计方案,只使用从160以后的96个数字,两个字节分成高位和低位,高位的取值范围从176~247共72个,低位从161~254共94个,这样,两个字节就有72*94=6768种可能,即可表示6768种汉字。
BG2312-80仅收录了6763个汉字,还有许多汉字没有被收录进去,为了对更多的字符进行编码,全国信息技术化技术委员会于1995年12月1日颁布了《汉字内码扩展规范》,简称GBK。在GBK1.0*收录了21886个汉字和图形符号,微软公司的window95系统的简体中文版开始即支持GBK编码。GBK向下与GB2312完全兼容,向上支持ISO10646国际标准。
(4)、UNICODE
每个国家和地区都规定了计算机信息交换编码,这就造成了不同编码国家、地区之间交流上的困难,如果全世界都使用统一的编码表就好了,为此UNICODE组织发布了UNICODE编码。这种编码使用双字节符号数对每一个字符进行编码,在UNICODE3.0.1中包含了49194个字符,将来,UNICODE中还会增加更多的字符。UNICODE的全称是“Universal Multiple-Octet Coded Character Set”,简称UCS。
(5)、UTF-8
使用UNICODE编码,一个英文字符也要占据两个字节,对于英文信息而言就增加了一倍数据量,为了减少存储和传输英文数据的数据量,美国人又制定了一系列用于传输和保存UNICODE的编码标准UTF,这些编码称为UCS传输格式码,也就是将UCS的编码通过一定的转换来达到使用的目的。常见的有UTF-7、UTF-8、UTF-16等。其中UTF-8编码得到了广泛的应用,UTF-8的全名是UCS Transformation Format 8,即UCS编码的8位传输格式,就是使用单字节的方式对UCS进行编码,使UNICODE编码能够在单字节的设备上正常进行处理。
UTF-8编码是变长的编码,对不同的UNICODE可能编成不同的长度。
从理论上来说,这些根据字符集设置而进行的字符转换不应该产生太多的问题,而事实上由于应用程序的实际运行环境不同,UNICODE和各个本地字符集的补充、完善以及系统或应用程序实现的不规范,转码时还是会经常出现问题而导致乱码。
2、乱码产生的原因
Java语言内部是用UNICODE表示字符的,遵守UNICODE V2.0。Java程序无论是以字符流读/写磁盘文件,还是向URL转接写HTML信息,或从URL连接读取参数值,都会由UNICODE作为中介和本地字符编码进行转换。
在WEB应用中,浏览器、WEB服务器、WEB应用程序和数据库等各个部分都有可能使用不用的字符集,字符在不同的字符集之间进行转换时,就有可能出现乱码问题。例如一个中文字符“中”要转换为ISO-8859-1编码,在Java中先读取到的是中文字符的GBK编码“0xD6D0”,转换为UNICODE码为“0x4E2D”,再从UNICODE编码转换ISO-8859-1编码,如果ISO-8859-1编码中没有对应的“0xD6D0”,于是就得到“0x3F”,也就是我们经常在页面上看到一堆“?”的原因。
在WEB应用中,乱码可能在多个环节产生,下面针对可能出现乱码的几个环节,给出解决的方案。
3、乱码解决方案
(1)、JSP页面最基本的乱码问题
运行下面的代码,会发现页面上出现的是乱码。
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html; charset=ISO8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>中文问题</title>
</head>
<body>
乱码问题
</body>
</html>
这段代码产生乱码的原因在于它有三处设置了编码格式,但是格式设置的不一致,因此导致了乱码,下面来分析这三处设置编码格式的作用。
<%@ page language="java" pageEncoding="UTF-8"%>
此处的pageEncoding="UTF-8"为JSP文件的存储格式。
<%@ page contentType="text/html; charset=ISO8859-1"%>
此处charset=ISO8859-1为JSP的解码格式。ISO8859-1是没有为汉字编码的,因此按UTF-8编码存储的文件如果用ISO8859-1编码格式编码,其中的中文字符就会因为找不到对应的编码而显示为乱码。所以JSP文件的存储格式和它的解码格式应该是一致的。
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
此处编码控制的是浏览器的解码方式,浏览器收到的只是一个字节流,它并不知道
页面是如何编码的,因此,需要一个机制来告诉浏览器页面的编码类型,标准的机制是使用<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">来指定页面的编码,当浏览器读取页面遇到这样的指示时,将使用这里制定的编码方式重新加载页面。
因此只要把三处的都设置为UTF-8或GBK即可解决乱码问题。
(2)、表单使用POST提交方式提交后接收到的是乱码
WEB容器在内部编码格式是ISO8859-1,在POST方式提交时,默认的提交编码格式是ISO8859-1,这样接收到的中文信息就会是乱码,解决方式如下:
在请求页面上开始处,执行请求和编码代码:
request.setCharacterEncoding(“GBK”);
把提交内容的字符集高为GBK,这样接受此参数的页面就不必再转码了,直接使用即可得到汉字参数。
在每一个接受提交的JSP页面及Servlet中都加这样的代码是比较烦人的,可以用过滤器设置request和response的setCharacterEncoding方法来解决这个问题。
例:过滤器解决问题
public class setEncodeingFilter extends HttpServlet implements Filter {
private FilterConfig config;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("GBK");
response.setCharacterEncoding("GBK");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
}
这个过滤器中已经在doFilter方法中直接设置了统一使用GBK编码,然后在web.xml国配置过滤器:
<filter>
<filter-name>setEncodeingFilter </filter-name>
<filter-class>setEncodeingFilter(过滤器的全路径)</filter-class>
</filter>
<filter-mapping>
<filter-name>setEncodeingFilter </filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<url-pattern>/*</url-pattern>表示所有的页面都要经过此过滤器过滤,这样就不用在JSP和Servlet中设置encoding了。
(3)、表单使用GET方式导致的乱码的处理方式
如果使用GET方式提交中文,接受参数的页面也会出现乱码,原因是web容器会以GET方式的默认编码方式ISO8859-1对汉字进行编码,编码后追加到URL,导致接受页面得到的参数为乱码。 因为在进入URL之前已经进行了ISO8859-1的编码处理,所以需要在得到参数值后进行编码转换:
String name = request.getParameter("name");
name = new String(name.getBytes("ISO8859-1"),"GBK");
(4)、数据库中读取和存储中文时的乱码问题
大多数的JDBC驱动都是默认IS-O8859-1为数据的传输编码格式,而数据库本身又有自己的字符集,因此在数据库读写中文数据库时也经常会出现筹码人。
流行的关系数据库系统都支持数据库Encoding,即在创建数据库时可以指定其自己的字符集设置。数据库的数据以指定的编码形式存储。当应用程序访问数据时,在入口和出口处都会有Encoding转换。对于中文数据,数据库字符编码的设置应当保证数据的完整性。
GB23212、GBK和UTF-8等都是可选的数据库Encoding,在JSP/Servlet编程时,可以先用数据库管理系统提供的管理功能检查其中的中文数据是否正确。
在计算机中任何数据都是以二进制存储的,要存储一个字符就要对它进行编码,用一个二进制数与这对应,这种对应的规则,就是字符的编码。编码的规则有很多种,一种规则所编码的“字符”的集合就叫做“字符集”。在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的,因此,平时我们所说的“字符集”,例如GB2312、GBK和JIS等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。
最早出现的编码是ASCII码,因为早期计算机系统只支持英语。后来每个国家(或区域)规定了计算机信息交换用的字符编码集,例如中国的GB2312等作为自己国家/区域内信息处理的基础。在程序读取字符到输出字符的过程中,就需要在不同的字符集之间进行转换,这个时候就容易出现乱码,因此要了解乱码是如何产生的,首先要了解各种字符编码。下面对这些编码做一个简单介绍。
(1)、ASCII
ASCII码是《美国标准信息交换码》,简称ASCII,它总共规定了128个字符号所对应的数字代码,使用了7位二进制的位来表示这些数字。其中包含了英文的大小写字母、数字和标点符号常用的字符,数字代号从0~127。
(2)、ISO8859
ASCII码解决了英语国家的字符问题,可是欧洲各个国家的字符问题还没有解决,例如法语中就有许多英语中没有的字符,为了解决该问题,国际标准化组织的ISO8859标准应运而生,在ISO8859的编码表中,编号0~127与ASCII保持兼容,编号128~159共32个编码保留给扩充定义的32个扩充控制码,160为空格,161~255的95个数字用于新增加的字符代码。由于在一张码表中只能增加95种字符的代码,因此ISO8859实际上不是一张码表,而是一系列标准,包括14个字符码表。例如,西欧的常用字符就包含在ISO8859-1字符表中,在ISO8859-7中则包含了ASCII和现代希腊语字符。
(3)、GB2312和GBK
GB2312是中国国家标准汉字信息交换用编码,简称国标码,标准号为GB2312-80。
中国的文字不是拼音文字,汉字的个数的数万之多,远远超过区区256个字符,ISO8859无能为力,但是通过借鉴ISO8859的编码思想,研究人员解决了中文的编码问题。GB2312使用两个字节来表示一个中文,在每个字符的256种可能中,为了与ASCII保持兼容,低于128的我们不使用。借鉴ISO8859的设计方案,只使用从160以后的96个数字,两个字节分成高位和低位,高位的取值范围从176~247共72个,低位从161~254共94个,这样,两个字节就有72*94=6768种可能,即可表示6768种汉字。
BG2312-80仅收录了6763个汉字,还有许多汉字没有被收录进去,为了对更多的字符进行编码,全国信息技术化技术委员会于1995年12月1日颁布了《汉字内码扩展规范》,简称GBK。在GBK1.0*收录了21886个汉字和图形符号,微软公司的window95系统的简体中文版开始即支持GBK编码。GBK向下与GB2312完全兼容,向上支持ISO10646国际标准。
(4)、UNICODE
每个国家和地区都规定了计算机信息交换编码,这就造成了不同编码国家、地区之间交流上的困难,如果全世界都使用统一的编码表就好了,为此UNICODE组织发布了UNICODE编码。这种编码使用双字节符号数对每一个字符进行编码,在UNICODE3.0.1中包含了49194个字符,将来,UNICODE中还会增加更多的字符。UNICODE的全称是“Universal Multiple-Octet Coded Character Set”,简称UCS。
(5)、UTF-8
使用UNICODE编码,一个英文字符也要占据两个字节,对于英文信息而言就增加了一倍数据量,为了减少存储和传输英文数据的数据量,美国人又制定了一系列用于传输和保存UNICODE的编码标准UTF,这些编码称为UCS传输格式码,也就是将UCS的编码通过一定的转换来达到使用的目的。常见的有UTF-7、UTF-8、UTF-16等。其中UTF-8编码得到了广泛的应用,UTF-8的全名是UCS Transformation Format 8,即UCS编码的8位传输格式,就是使用单字节的方式对UCS进行编码,使UNICODE编码能够在单字节的设备上正常进行处理。
UTF-8编码是变长的编码,对不同的UNICODE可能编成不同的长度。
从理论上来说,这些根据字符集设置而进行的字符转换不应该产生太多的问题,而事实上由于应用程序的实际运行环境不同,UNICODE和各个本地字符集的补充、完善以及系统或应用程序实现的不规范,转码时还是会经常出现问题而导致乱码。
2、乱码产生的原因
Java语言内部是用UNICODE表示字符的,遵守UNICODE V2.0。Java程序无论是以字符流读/写磁盘文件,还是向URL转接写HTML信息,或从URL连接读取参数值,都会由UNICODE作为中介和本地字符编码进行转换。
在WEB应用中,浏览器、WEB服务器、WEB应用程序和数据库等各个部分都有可能使用不用的字符集,字符在不同的字符集之间进行转换时,就有可能出现乱码问题。例如一个中文字符“中”要转换为ISO-8859-1编码,在Java中先读取到的是中文字符的GBK编码“0xD6D0”,转换为UNICODE码为“0x4E2D”,再从UNICODE编码转换ISO-8859-1编码,如果ISO-8859-1编码中没有对应的“0xD6D0”,于是就得到“0x3F”,也就是我们经常在页面上看到一堆“?”的原因。
在WEB应用中,乱码可能在多个环节产生,下面针对可能出现乱码的几个环节,给出解决的方案。
3、乱码解决方案
(1)、JSP页面最基本的乱码问题
运行下面的代码,会发现页面上出现的是乱码。
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html; charset=ISO8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>中文问题</title>
</head>
<body>
乱码问题
</body>
</html>
这段代码产生乱码的原因在于它有三处设置了编码格式,但是格式设置的不一致,因此导致了乱码,下面来分析这三处设置编码格式的作用。
<%@ page language="java" pageEncoding="UTF-8"%>
此处的pageEncoding="UTF-8"为JSP文件的存储格式。
<%@ page contentType="text/html; charset=ISO8859-1"%>
此处charset=ISO8859-1为JSP的解码格式。ISO8859-1是没有为汉字编码的,因此按UTF-8编码存储的文件如果用ISO8859-1编码格式编码,其中的中文字符就会因为找不到对应的编码而显示为乱码。所以JSP文件的存储格式和它的解码格式应该是一致的。
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
此处编码控制的是浏览器的解码方式,浏览器收到的只是一个字节流,它并不知道
页面是如何编码的,因此,需要一个机制来告诉浏览器页面的编码类型,标准的机制是使用<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">来指定页面的编码,当浏览器读取页面遇到这样的指示时,将使用这里制定的编码方式重新加载页面。
因此只要把三处的都设置为UTF-8或GBK即可解决乱码问题。
(2)、表单使用POST提交方式提交后接收到的是乱码
WEB容器在内部编码格式是ISO8859-1,在POST方式提交时,默认的提交编码格式是ISO8859-1,这样接收到的中文信息就会是乱码,解决方式如下:
在请求页面上开始处,执行请求和编码代码:
request.setCharacterEncoding(“GBK”);
把提交内容的字符集高为GBK,这样接受此参数的页面就不必再转码了,直接使用即可得到汉字参数。
在每一个接受提交的JSP页面及Servlet中都加这样的代码是比较烦人的,可以用过滤器设置request和response的setCharacterEncoding方法来解决这个问题。
例:过滤器解决问题
public class setEncodeingFilter extends HttpServlet implements Filter {
private FilterConfig config;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("GBK");
response.setCharacterEncoding("GBK");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
}
这个过滤器中已经在doFilter方法中直接设置了统一使用GBK编码,然后在web.xml国配置过滤器:
<filter>
<filter-name>setEncodeingFilter </filter-name>
<filter-class>setEncodeingFilter(过滤器的全路径)</filter-class>
</filter>
<filter-mapping>
<filter-name>setEncodeingFilter </filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<url-pattern>/*</url-pattern>表示所有的页面都要经过此过滤器过滤,这样就不用在JSP和Servlet中设置encoding了。
(3)、表单使用GET方式导致的乱码的处理方式
如果使用GET方式提交中文,接受参数的页面也会出现乱码,原因是web容器会以GET方式的默认编码方式ISO8859-1对汉字进行编码,编码后追加到URL,导致接受页面得到的参数为乱码。 因为在进入URL之前已经进行了ISO8859-1的编码处理,所以需要在得到参数值后进行编码转换:
String name = request.getParameter("name");
name = new String(name.getBytes("ISO8859-1"),"GBK");
(4)、数据库中读取和存储中文时的乱码问题
大多数的JDBC驱动都是默认IS-O8859-1为数据的传输编码格式,而数据库本身又有自己的字符集,因此在数据库读写中文数据库时也经常会出现筹码人。
流行的关系数据库系统都支持数据库Encoding,即在创建数据库时可以指定其自己的字符集设置。数据库的数据以指定的编码形式存储。当应用程序访问数据时,在入口和出口处都会有Encoding转换。对于中文数据,数据库字符编码的设置应当保证数据的完整性。
GB23212、GBK和UTF-8等都是可选的数据库Encoding,在JSP/Servlet编程时,可以先用数据库管理系统提供的管理功能检查其中的中文数据是否正确。
上一篇: 随机生成图片验证码
下一篇: java用jxl包导出Excel的例子
推荐阅读
-
PHP常用编译参数中文说明,
-
Thinkpad x220 无法待机和无法更改bios设置的问题解决
-
C++学习笔记(字符串string、vector_deque、queue,multiset、map、multimap、容器拷贝问题)(复制粘贴,方便后面翻阅)
-
安装Oracle 11GR2 +RedHat AS4遇到的问题
-
MySQL乱码问题终极指南
-
MySQL('root'@'%')doesnotexist问题的解决办法
-
vue router路由嵌套不显示问题的解决方法
-
curl函数的中文档案
-
百度云推送的问题?
-
iOS-UITableView 之 tableHeaderView 遮挡后面 cell 的问题