字符集设置问题深究

1、基础知识

字符(Character):是文字和符号的总称。例如'A'、'B'、'汉'、'$'等。

字符集(Charset):是一个系统支持的所有抽象字符的集合。

字符编码(Character Encoding):是一套法则,描述字符集与数字系统之间建立对应关系。给定一系列字符,对每个字符赋予一个数值,用数值来代表对应的字符,这一数值就是字符的编码(Encoding)。例如,我们给字符'A'赋予数值0,给字符’B'赋予数值1,则0就是字符'A'的编码。

字符序(Collation):是指在同一字符集内字符之间的比较规则;确定字符序后,才能在一个字符集上定义什么是等价的字符,以及字符之间的大小关系;每个字符序唯一对应一种字符集,但一个字符集可以对应多种字符序。


mysql字符序的命名规则:字符集名字_语言_后缀,
几种常见后缀:_ci(表示大小写不敏感)、_cs(表示大小写敏感)、_bin(表示按编码值比较)
例如:在字符序“utf8_general_ci”下,字符“a”和“A”是等价的;


2、常用字符集和字符编码

2.1.ASCII、Latin-1

ASCII字符集:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。

ASCII编码:将ASCII字符集转换为计算机可以接受的数字系统的数的规则。使用7位(bits)表示一个字符,共128字符;但是7位编码的字符集只能支持128个字符。


ISO-8859-1又称Latin-1:是一个8位单字节字符集,它把ASCII的最高位也利用起来,并兼容了ASCII,新增的理论空间是128,但它并没有完全用完。



2.2.GB2312、GBK

GB2312字符集:中国国家标准简体中文字符集,包括基本汉字,缺少罕用字。

GBK字符集:GB2312字符集的扩展,支持中国国内少数民族的文字,汉字收录范围包含繁体汉字以及日韩汉字。

GBK编码:采用多字节编码。


2.3.Unicode

Unicode字符集:世界上所有的符号纳入其中,每一个符号都给予一个独一无二的编码,以满足跨语言、跨平台进行文本转换、处理的要求。

Unicode编码:计算机一般使用 4个字节(32 位)(两个字节不够用时)来存放一个序号,该序号为每个字符在 UNICODE 字符集中的序号。


2.4.UTF

UTF:是Unicode 的实现(或存储)方式,称为Unicode转换格式。Unicode是字符集,UTF-32/ UTF-16/ UTF-8是三种字符编码方案。

UTF-8编码:是一种针对Unicode的可变长度字符编码,也是一种前缀码。

Utf-8 中文三个字节,英文一个字节 

Unicode 中文两个字节,英文两个字(不够用时4字节)

gb2312,gbk 中文两个字节,英文一个字节 


Unicode编码转换为UTF-8编码:

Unicode         UTF-8
0000 - 007F    0xxxxxxx
0080 - 07FF    110xxxxx 10xxxxxx
0800 - FFFF    1110xxxx 10xxxxxx 10xxxxxx


        为国际化,许多操作系统,应用开发语言,以及平台都走过了漫长的道路。有些事情很容易,比如在Swing 文本框中输入你的姓名。通过键盘,输入方法,和主机软件协作来创建正确的字符,无论你的名字是John,José 。不幸的是,虽然在浏览器中输入非ASCII文本与你在Swing组件中一样容易,但通过web精确传送它却是复杂的。因为没有工业标准来控制应用程序如何在GET或者POST方法中对数据进行编码,在多个编程接口传输过程中数据可能被转换成无意义的乱码。而且,web服务器和数据库管理者通常对字符编码都了解甚少,这样就影响了文本高保真的从浏览器到数据库的传输。


3、浏览器显示和表单提交

只要HTML页面为浏览器解析字符提供了足够的选择和使用合适的字体及编码的提示,现代浏览器就能够正确显示大多数文本。如果你用JSP技术生成页面,仍然必须指示生成的HTML页面的字符编码。你可以使用JSP的page指令或HMTL的meta标签。

<%@ page language="java" contentType="text/html; charset=utf-8"  pageEncoding="utf-8"%>  
<html>  
<head>  
<title>中文问题</title>  
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
</head>  
<body>  
中文乱码问题  
</body>  
</html>  
</span> 

pageEncoding="utf-8":jsp文件的存储格式,Eclipse会根据这个编码格式保存文件,Eclipse里面有默认的jsp编码格式,查看方式如下:window -> Preferences -> Web -> Jsp Files或右键-> Preferences,新建jsp文件编码格式都是默认的,若在文件中改变pageEncoding的值,则Eclipse会以jsp文件中pageEncoding新值的编码格式保存jsp文件,并编译jsp文件,包括里面的汉字。

contentType="text/html;charset=UTF-8"的charset:是指服务器发送给客户端时的内容编码。也就是Web服务器向请求资源的浏览器发送jsp文件的时候,jsp文件此时编码格式是charset所指定的编码格式,不一定与pageEncoding指定编码格式一致,中间会有编码转换过程。HTTP头部的contentType属性值就是这个。


第一阶段:jsp编译成.java,它会根据pageEncoding的设定读取jsp,结果是由指定的编码方案翻译成统一的UTF-8 JAVA源码(即.java),如果pageEncoding设定错了,或没有设定,出来的就是中文乱码。

第二阶段:是由JAVAC的JAVA源码至java byteCode(java字节码)的编译,不论JSP编写时候用的是什么编码方案,经过这个阶段的结果全部是UTF-8的encoding的java源码(.class文件都是utf-8编码的)。

第三阶段:就是由Tomcat出来的网页,用的是contentType指定的编码格式,会进行一次utf-8 --> contentType 的编码转换,最终浏览器以contentType编码方式解析传过来的jsp文件。

content="text/html; charset=UTF-8":把 content 属性关联到 HTTP 头部。jsp页面page指令中有指定contentType,则此时meta中content无效,HTTP头部contentType属性以page指令中有指定contentType为准。html中meta属性content是有效的。


此时页面能够正确编码了,并且它们可以与浏览器交流信息了。但是仍然没有从浏览器向服务器传过来任何东西。正如已经展示的那样,浏览器从保存在HTTP header中charset信息或者HTML中<meta>标签知道了页面的编码信息。当通过GET或者POST向服务器返回信息时,它们使用相同的信息对form表单中的数据进行编码。如果在HTML的form表单标签中有一个accpet-charset属性,浏览器将使用该设置。也就是说,浏览器使用页面或者form表单本身编码方式对form表单中的数据进行编码。 

更详细的测试信息请看:各种编码UNICODE、UTF-8、ANSI、ASCII、GB2312、GBK详解   字符从浏览器到数据库过程的转换


4、Web服务器处理过程 

        尽管当前的浏览器能根据页面或者form表单中提示把form中数据使用相同的编码返回给服务器,但大多数服务器仍认为浏览器不知道如何选择字符。它们典型假设浏览器编码是ISO 8859-1。即使一个应用经历了用UTF-8 来对GET参数进行URL编码的麻烦,服务器仍将假设使用8859-1。这就导致了字符通过多个web应用层时出现了乱码。现在还没有公布的标准规定(浏览器和服务器)如何沟通关于GET和POST数据的字符集选择。如何解决呢?

解决方案:
1.对于GET请求:Tomcat中通过在server.xml文件里面的connector(连接器)中设置URIEncoding="UTF-8"来告诉web服务器如何选择字符编码,这样Tomcat就能正确读取URL中的GET参数了。但Tomcat不使用URIEncoding标签来处理POST过来的form表单数据。

2.对于POST请求:在请求页面上开始处,执行请求的编码代码, request.setCharacterEncoding("UTF-8"),把提交内容的字符集设为UTF-8(同样适用于GET请求)。
为了避免每页都要写request.setCharacterEncoding("UTF-8"),建议使用过滤器对所有jsp进行编码处理。

public class MsgHandler extends HttpServlet {     
     
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)  
    throws ServletException, IOException {  
        if (encoding != null) {  
            request.setCharacterEncoding("utf-8");  
        }  
    }  
    protected void doGet(HttpServletRequest request, HttpServletResponse response)  
    throws ServletException, IOException {  
        processRequest(request, response);  
    }  
    protected void doPost(HttpServletRequest request, HttpServletResponse response)  
    throws ServletException, IOException {  
        processRequest(request, response);  
    }  
} 

现在,无论你GET或POST数据,你的JSP或servlet都能正确读取参数,因为你已经明确告诉web服务器处理请求时使用哪种字符编码。


5、数据库连接设置

前面web服务器能够正确获取从浏览器表单传过来的字符,剩下的任务就是将字符正确的存入数据库。

mysql系统变量:
– character_set_server:默认的内部操作字符集 
– character_set_client:客户端来源数据使用的字符集 
– character_set_connection:连接层字符集 
– character_set_results:查询结果字符集 
– character_set_database:当前选中数据库的默认字符集 
– character_set_system:系统元数据(字段名等)字符集 
– 还有以collation_开头的同上面对应的变量,用来描述字符序。

mysql系统变量的设置、查看、修改:

系统变量的设置:

 set character_set_client=utf8;
 set character_set_connection=utf8;
 set character_set_results=utf8;

 set names 'utf8';与前面三条语句等价!


系统变量的查看:

SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';


修改默认字符集

(1) 最简单的修改方法,就是修改mysql的my.ini文件中的字符集键值,
     如    default-character-set = utf8
      character_set_server =  utf8
     修改完后,重启mysql的服务
(2) 还有一种修改字符集的方法,就是使用mysql的命令
     mysql> SET character_set_client = utf8 ;
     mysql> SET character_set_connection = utf8 ;
     mysql> SET character_set_database = utf8 ;
     mysql> SET character_set_results = utf8 ;
     mysql> SET character_set_server = utf8 ;
     mysql> SET collation_connection = utf8 ;
     mysql> SET collation_database = utf8 ;
     mysql> SET collation_server = utf8 ;


客户端发送SQL语句如查询,通过连接发送到服务器,服务器通过连接发送响应给客户端如结果集。对于客户端连接,这样会导致一些关于连接的字符集和校对规则的问题,这些问题均能够通过系统变量来解决:


i  当查询离开客户端后,在查询中使用哪种字符集?
    character_set_client变量“声明告知”服务器客户端的字符集是什么,它和字符串文字的引介词起着同样的作用。注意,是“声明”,故实际上有可能不是,即客户端可以用这个“声明”来欺骗服务器,从而“隐藏”自己真正的字符集。

ii  服务器接收到查询后应该转换为哪种字符集?
    当服务器接到客户端发来的语句后,它会将“这串字符”做一层字符编码的映射转换。它会把字符串从character_set_client字符集转换到character_set_connection字符集

iii 服务器发送结果集或返回错误信息到客户端之前应该转换为哪种字符集?
    character_set_results变量指示服务器返回查询结果到客户端使用的字符集。包括结果数据,例如列值和结果元数据(如列名)。

iv  字符串最后存储是哪种字符集?
字符串最终保存下来是什么字符集,这个是分别受到server、db、table和column四个级别的定义控制的,它们决定了字符串最终以什么字符集存储。

v   不同字符集之间会发生转换吗?
当字符串在不同字符集之间“穿梭”时,会发生字符集编码的映射转换,如果发生相互转换的两种字符集不是相互兼容的,那么字符串就有可能会发生丢失。


mysql最终以何种形式来存储字符串的呢?(详情查看:mysql 4个级别的字符集和校验规则)

进行内部操作前将请求数据从character_set_connection转换为内部操作字符集 character_set_server,其确定方法如下:
       - 使用每个数据字段的CHARACTER SET设定值;
       - 若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);
       - 若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;
       - 若上述值不存在,则使用character_set_server设定值。

四个级别的字符集和校验规则的指定(级别高低server>database>table>column)
1、本级别中,如果同时指定character set和collate,那么就使用指定的值;
2、本级别中,只指定character set而没有指定collate,那么就使用指定的character set值和其对应的默认的collation值;
3、本级别中,只指定collate而没有指定character set,那么就使用指定的collation值和其所对应的character set值;
4、本级别中,都没有指定这2个值,那么默认就使用上一级别的对应的值。 


mysql数据库乱码情况分析:

1.向默认字符集为utf8的数据表插入utf8编码的数据前没有设置连接字符集,查询时设置连接字符集为utf8

即是客户端sql语句格式为utf8,插入数据时mysql服务器当做默认character_set_client=latin1来处理 。查询时SET names = utf8 ;即是character_set_results = utf8 ;装换过程如下:



2.向默认字符集为latin1的数据表插入utf8编码的数据前设置了连接字符集为utf8

插入数据时mysql服务器当做SET names = utf8 来处理 ,数据存储时有utf8向latin1转换的过程,数据存储转化过程出现乱码。


注:MYSQL Client 有很多种:客户端是看访问mysql 数据库的方式,通过命令行访问,命令行窗口就是客户端,通过JDBC 等连接访问,程序就是客户端.

mysql workbench:  查看其发送mysql语句所用字符集编码可用 select hex('中'); 3个字节为utf8、 2个字节为gbk

MySQL 5.5 Command Line Client:也就是cmd窗体 默认发送mysql语句所用字符集编码为GBK,也可用 select hex('中'); 查看

java 程序:发送的mysql语句大多是utf8编码格式,jdbc可以设置jdbc:mysql://localhost:3306/abs?useUnicode=true&characterEncoding=utf8

通过JDBC访问数据库时JVM对字符集的处理:
JVM核心完全使用Unicode字符集,编码上采用UTF-16LE(x86和Unix)。 Java编译器扫描.java源文件时将完成预转换,比如在中文Windows上编译.java文件时,你可能已经注意到.java文件中的字符串和.class中的不一样。因为.java文件本身用的是gb2312编码,而.class内则是UTF-16LE编码。如果你的编辑器支持,你可能会选择直接用UTF-8来书写.java源程序,这时Java编译器就会用UTF-8对源程序解码。在输出时,比如调用System.out.print方法也将完成一个编码转换,在上述情况中经常是将内存中的UTF-16LE编码的字串转换成控制台上可读的gb2312编码。


6、总结

为防止乱码出现,最好统一各个容易出现转码问题的编码格式:

1.在jsp部分,头部加上<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>,在html头部加上<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />,如果有request的话,request.setCharacterEncoding("utf-8")

2.CSS如果乱掉了,在CSS文件顶部加上@charset "utf-8"(最好在将该文件以记事本方法打开,以utf-8保存覆盖掉原来的)

3.在调用js文件如果发现无法函数的时候,将该文件以记事本方法打开,以utf-8保存覆盖掉原来的

4.我用的数据库是MySQL5.0,首先在工程初始化DB连接的时候加上jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8,在数据库表和相应的字段都要的charset要选择utf8(字段也要加)。或者再配置文件my.ini中做如下修改:

[mysql]default-character-set=utf8  

[mysqld]default-character-set=utf8 

(init_connect='SET NAMES utf8' default-collation = utf8_general_ci)。


参考来源:

设置mysql的连接通道的字符集和校验规则 

mysql字符集和校对规则(Mysql校对集)

深入Mysql字符集设置分析

Eclipse,tomcat及jsp页面编码的设定解决的中文乱码问题

字符从浏览器到数据库过程的转换

字符集与字符编码简介

各种编码UNICODE、UTF-8、ANSI、ASCII、GB2312、GBK详解




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值