• Web开发中的字符集问题和解决方案


    很多人在开发网站开始阶段没有注意到字符集统一的重要性,因此开发的网站不是数据库中存有乱码就是web页面显示为乱码。现以web开发中的字符集为主体讨论在个人和团队web开发中需要注意的问题,和相应的解决方法。

    1. 常见问题的解决

    比如我在网站开发阶段就遇到两个严重的问题:

    我使用的是Zend studio的开发工具,保存代码时也是默认保存的,没有注意到字符集的问题,开始一切正常,但是当我想在页面中添加’©’这个符号的时候,网页不能正常显示。后来发现,zend 默认的保存编码是gb2321编码,而这个编码中不能表示以上那个字符,要显示那个字符有两种方式,一种是使用php中的图形函数,将copyright字符转换为图形显示在网页上。第二种方法是将网站格式完全转化为utf-8编码。我采用的是后者,将全部页面保存为utf-8格式,然后在content-type中将charset=转换为utf-8;

    <o:p> </o:p>

    第二个严重的问题是数据库的乱码问题。再将文章存入数据库中后,使用phpmyadmin查看其中数据显示的也全部是乱码,起初没有对这个完全正视起来,因为在php使用其中数据时没有出现异常,在网页中显示正常。但是这本就是个严重的隐患,因此今天想办法解决了,解决方法使用的是很笨的方法,将数据库中的数据读出来,存入表中,然后写一个安装文件(使用php),再连接数据库后,添加一段代码,效果如下:

    $conn=new mysqli(hostname,username,passwd,dbname);

    $conn->query(‘set names \’utf8\’);

    这样在将php的安装文件中的数据写入数据库时就不会出现编码不一致的问题。在phpMyadmin中也能正常显示了。

     

    2.原理介绍

    (摘自阿强的blog: http://www.phpchina.com/10458/viewspace_4782.html)

    1. Collations
    Collations翻成中文是“校验”,在网页开发的过程中,这个词汇,只在Mysql里使用,主要作用是指导Mysql对字符的比较,比如, ASCII字符集里,Collations规定了a小于b,a等于a,以及a是否等于A之类的。通常,大家基本可以忽略Collations的存在,因为每个字符集都有一个默认的Collations,通常,使用默认的Collations就可以了。以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个Unicode编码是594EUnicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是还是<o:p></o:p>

    2.字符集
    与这对比的是,字符集是个更广的概念,即使是Windows下普通的文本文件,也渗及到字符集的问题。不同的字符集,规定了不同的字符的编码方式。一个 character set (字符集)是一组符号和编码,比如,ASCII字符集,包括的字符有:数字,大小写字母,分号、换行之类的符号,编码方式是用一个7bit表示一个字符(A的编码是65,b的编码是98)。ASCII只规定了英文字母的编码,非英文语言不能用ASCII编码表示,为此,不同的国家,都为自己的语言做了编码,比如,我们国家,就有GB2312编码。但每个国家之间的编码不同,也存在着一些跨平台的问题,为此,一些国际化标准组织,就制定了一些国际通用的编码,最常用的就是UTF8了。ASCII只对英文符号和英文字母做了编码,GB2312对英文符号,英文字母,汉字做了编码,UTF8对世界上所有的语言文字做了编码,所以,GB1212的字符包含了ASCII字符,UTF8包含了GB2312字符。由此可见,UTF8是所含最广字符的字符集,所以,在一些多语言的WEB系统中,一般用UTF8字符集(PHPMyAdmin使用UTF8编码)。
    任何文本的存储,都渗及到字符集的概念。包括数据库,也包括普通的文本文件。
    主要术语:
    字符:汉字,英文字母,标点符号,拉丁文等等。
    编码:将字符转换成计算机存储的格式,比如,A用65表示
    字符集:一组字符以及对应的编码方式。

    a. Mysql的字符集
    Mysql目前支持多字符集,并且,支持在不同的字符集之间转换(便于移植和支持多语言)。
    Mysql可以设置服务器级字符集、数据库级字符集、数据表级字符集、表列的字符集,实际上,最终使用字符集的地方是存储字符的列,比如,你设置 table1中col1列是字符类型,col1才用到了字符集,如果table1表的col2列是int类型,col2不使用字符集的概念。
    服务器级字符集、数据库级字符集、数据表级字符集都是为列的字符集做默认选项的。
    Mysql一定有一个字符集,可以通过启动时加参数指定,也可以编译时指定,也可以在配置文件里指定。Mysql服务器字符集,只是做为数据库级的默认值。创建数据库时,你可以指定字符集,如果没指定,就使用服务器的字符集。同理,创建表时,你可以指定表级的字符集,如果没指定,使用数据库的字符集做为表的字符集。创建列时,你可以指定某列的字符集,如果没指定,就使用表的字符集。
    通常情况下,您只需设置服务器级的字符集,其它的数据库级,表级,以及列级的字符集,都继承自服务器级字符集。
    由于UTF8是最广的字符集,所以,一般情况下,我们设置Mysql服务器级的字符集为UTF8!

    b. 普通文本的字符集问题
    任何文本的存储,都存在着字符集的问题,普通文本文件也不例外。
    Windows2000+的系统中,打开记事本,“保存为…”对话框,就有一个选项,可以让你选择存储文本的编码方式。
    通常情况下,大家都使用Windows2000+的系统,都使用默认的编码,所以,不会碰到字符集的问题。
    Windows下,保存文本文件时,可以选择编码方式,但打开文本文件时,都是自动判断编码方式的。网上有一个用Windows2000+的记事本玩移动,联通的笑话,大家可以搜搜,就是因为Windows在打开文本文件时,编码判断错误引起的问题。
    因为自动判断编码有时会错误,所以,有的文本文件,规定了如何识别自身所使用的编码。HTML文件就是一个这样的例子。>
    HTML是文本文件。存储HTML文件的时候,需要使用一个编码,并且,在HTML文件里,也使用HTML语法,指定了该文件所使用的编码(比如< meta http-equiv="content-type" content="text/html; charset=UTF-8">)。如果HTML文件没有指定编码,则浏览器自动识别文件的编码。如果HTML指定了编码,则浏览器使用HTML指定的编码。
    通常情况下,HTML文件指定的charset和HTML文件自身的编码是一致的,但也有不一致的情况,如果不一致,就会导致网页乱码(此处乱码,只和文本文件有关,和数据库无关。)使用专门的网页编辑工具(比如Dreamwave),会自动根据网页中的charset值来编码文件。

    c. php+mysql的字符集问题
    PHP最终生成的是文本文件,但他要取数据库里的文本,或将文本存进数据库
    由于Mysql支持多字符集,默认情况下,Mysql不知道PHP发给他的是什么编码的字符,所以,Mysql要求客户端(PHP)告诉他存取的字符集是什么。  
    PHP通过设置character_set_client,告诉Mysql,PHP存进数据库的是什么编码方式。
    PHP通过设置character_set_results,告诉Mysql,PHP需要取什么样编码的数据。
    PHP通过设置character_set_connection,告诉Mysql,PHP查询中的文本,使用什么编码。
    MYSQL使用设置的编码方式存储文本。  
    假设Mysql使用setserver来存储文本,PHP的character_set_client是setclient,PHP的character_set_results是setresult。那么,Mysql将PHP发来的文本,从setclient编码方式,转换成 setserver编码方式,再存入数据库,如果PHP取文本,Mysql将文本从setserver转换成setresult,再发送给PHP。
    PHP文件(最终生成的HTML文件)本身有个编码,如果Mysql传过来的编码,与PHP文件自身的编码不同,那么,整个网页,必然乱码。所以,PHP一般将自己的编码方式,告诉Mysql。
    要保证不乱码,就必须将三个编码统一:一是网页自身的编码,二是HTML里指定的编码,三是PHP告诉Mysql的编码(包括character_set_client和character_set_results)。
    第一和第二个编码,如果使用DW之类的编辑器写的网页,通常是一致的,但用记事本写的网页,有可能不一致。(这里我感觉不太准确,需要讨论一下)
    第三个编码,需要手工通知Mysql。这步可以通过在PHP里使用mysql_query(“set names characterX”)来实现。

    d.字符集的转换问题
    如果小字集转换成大字符集,不会丢失数据,但大字集,转换成小字集,可能会丢失数据。
    比如,UTF8里有的字符,GB2312不一定有,所以,从UTF8转换到GB2312可能会丢失一些字符。
    但有种情况例外,先从GB2312转成UTF8,再从UTF8转成GB2312,这种情况是不会丢数据的,因为,刚开始转换的文本,都是GB2312里的字符,所以,整个过程都是GB2312的字符在转换,不会丢失。
    正因为UTF8能容纳世界上的所有字符,所以,数据库一般使用UTF8编码。这使得,任何字符都可以存进UTF8编码的数据库。

    e. PHPMyAdmin乱码的问题
    PHPMyAdmin支持多国语言,这就必定要求HTML页面使用UTF8编码。
    HTML页面使用UTF8编码,这就必定要求PHPMyAdmin连接Mysql时,character_set_client和character_set_results使用UTF8编码。
    当前情况下,PHP连接Mysql只能是使用set names(或其它几个语句)来通知Mysql的编码方式,如果没有显式的声明编码方式,都将使用latin1编码。一般的程序,都没有显式声明 character_set_client变量,所以,都是将gb2312文本,按latin1编码方式存在数据库,PHPMyAdmin再用utf8格式读取,肯定是乱码的。
    如果PHP程序按正确的编码存入数据库,肯定是没有问题的。所以,需要修改的不是PHPMyAdmin.(虽然有时修改PHPMyAdmin可以解决乱码问题,但这不是问题的根本)

    补充:
    (参考:http://blog.csdn.net/fmddlmyy/archive/2005/05/04/372148.aspx

    UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?Unicode规范中推荐的标记字节顺序的方法是BOMBOM不是“Bill Of Material”BOM表,而是Byte Order MarkBOM是一个有点小聪明的想法:

    UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFEUCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"<o:p></o:p>

    这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM<o:p></o:p>

    UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

    Windows就是使用BOM来标记文本文件的编码方式的。

     

    讨论:

    第一和第二个编码,如果使用DW之类的编辑器写的网页,通常是一致的,但用记事本写的网页,有可能不一致。

    如果在dw中选择了:“包括Unicode签名(Bom)”那么,保存的和记事本保存的是一样的,都是前面多出三个字节。这三个字节在网页显示时有特殊的作用,如果这样保存一个网页,在html中不需要指定utf-8格式,浏览器就自动选用utf8编码,而且浏览者不能自己修改成其他的编码格式。如此保存在开发中可能是一个既保险又提高效率的好方法,但是考虑到他和其他很多开发工具的不兼容(zend中没有这个功能,而且显示代码的时候,将前三个字符显示为点,在editplus中全部显示为乱码。所以不推荐选用bom

     

    3.团队中的字符集的同步

    我们推荐使用utf-8字符集,因为此字符集将所有字符都包含了进去,因此以后不会再担心字符表示不了的问题。

    但是如何在团队开发重视全部参与编码的成员都保证自己的字符是utf-8编码的?我们需要考虑这些方面:开发工具的默认编码(也就是网页的编码),html中指定的编码,和php指定的编码,数据库的编码。

    对于开发工具默认的编码,需要考虑美工的开发工具和编程人员的开发工具,最普遍的为dreamweaverzend studio,最一劳永逸的方式不是在每次保存时指定编码格式,而是在编程工具的选项或者是首选项中指定字符集编码,对于dreamweaver,打开编辑菜单,选择首选参数(或Ctrl+U),选择新建文档分类(如下),在默认编码中选择如图的utf-8,然后将下面的“当打开未指定的”,在unicode标准化表单中选择c类,但是不要选择下面的“包括unicode签名”,具体问什么,上面已经说了。这样在每次保存的时候,就默认保存为utf编码。

    <v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path><o:lock v:ext="edit" aspectratio="t"></o:lock></v:shapetype><v:shape id="_x0000_i1025" style="WIDTH: 375pt; HEIGHT: 261pt" type="#_x0000_t75" alt="" o:button="t"><v:imagedata src="file:///F:\DOCUME~1\xombat\LOCALS~1\Temp\msohtml1\01\clip_image001.gif" o:href="http://www.cnbruce.com/blog/uploadfile/GIF/2006-7/28-16727-dw-utf-8.gif"></v:imagedata></v:shape>

    对于php开发人员,使用zend ,选择tool->preference…进入preference对话框,选择Desktop标签,然后在Encoding文本框中选择UTF-8.在进入Debug标签,从中选定define server out put encoding ,然后在后面的文本框中选择utf-8(这一步主要是为了在zend开发环境中调试而用)

    html中指定的编码

    保存的文本是utf-8的还不够,我们还需要在html中指定编码类型,具体有两种方法,可以任选其一:  

    Html代码方法:在 标签中加入这一行: <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    Php代码方法:在输出任何数据之前,加入这行代码:header(‘content-type:text/html;charset=utf<st1:chmetcnv tcsc="0" numbertype="1" negative="True" hasspace="False" sourcevalue="8" unitname="’" w:st="on">-8’</st1:chmetcnv>);

    两种方法任选其一。

    Php指定编码:

    需要在连接好数据库后,加入如下一行代码, 

    $conn->query(‘set names 'utf8');

    具体见第一部分问题解决。

    数据库的编码:

    这里数据库的编码,指的是数据库中默认的编码。 默认的编码方式使用php的函数可以查看(连接数据库后,$charset=$conn->character_set(),也可以通过查看mysql系统变量来查看; $charset就是编码类型的信息)。设定数据库的默认编码方式是在mysql 程序文件的my.ini中修改如下的指令:

    将所有的都改为:default-character-set=utf8

    mysqld下面添加:default-collation=utf8_general_ci(其实这行不添加也可以,utf-8默认的就是这个)

     

    原文:http://www.javaeye.com/topic/88888

  • 相关阅读:
    Python挂载杂记
    Python Log的使用, 模块化
    Python实现图片长宽比例缩放和填充
    系统监控与硬盘
    linux基础三
    linux基础二
    linux基础一
    网络基础
    操作系统
    计算机硬件基础
  • 原文地址:https://www.cnblogs.com/keenhome/p/1935310.html
Copyright © 2020-2023  润新知