北京大学论坛
标题:
PHP 开发中的中文编码问题
[打印本页]
作者:
songlai
时间:
2007-11-21 23:11
标题:
PHP 开发中的中文编码问题
PHP
程序设计中
中文
编码问题曾经困扰很多人,导致这个问题的原因其实很简单,每个国家
(
或区域
)
都规定了计算机
信息
交换用的字符编码集,如美国的扩展
ASCII
码
,
中国的
GB2312-80
,日本的
JIS
等。作为该国家
/
区域内信息处理的基础,字符编码集起着统一编码的重要作用。字符编码集按长度分为
SBCS(
单字节字符集
)
,
DBCS(
双字节字符集
)
两大类。早期的软件
(
尤其是
操作系统
)
,为了解决本地字符信息的计算机处理,出现了各种本地化版本
(L10N)
,为了区分,引进了
LANG, Codepage
等概念。但是由于各个本地字符集
代码
范围重叠,相互间信息交换困难
;
软件各个本地化版本独立维护成本较高。因此有必要将本地化工作中的共性抽取出来,作一致处理,将特别的本地化处理内容降低到最少。这也就是所谓的国际化
(118N)
。各种语言信息被进一步规范为
Locale
信息。处理的底层字符集变成了几乎包含了所有字形的
Unicode
。
现在大部分具有国际化特征的软件核心字符处理都是以
Unicode
为基础的,在软件运行时根据当时的
ocale/Lang/Codepage
设置确定相应的本地字符编码设置,并依此处理本地字符。在处理过程中需要实现
Unicode
和本地字符集的相互转换,甚或以
Unicode
为中间的两个不同本地字符集的相互转换。这种方式在网络环境下被进一步延伸,任何网络两端的字符信息也需要根据字符集的设置转换成可接受的内容。
数据库中的字符集编码问题
流行的关系数据库系统都支持数据库字符集编码,也就是说在创建数据库时可以指定它自己的字符集设置,数据库的数据以指定的编码形式存储。当应用程序访问数据时,在入口和出口处都会有字符集编码的转换。对于中文数据,数据库字符编码的设置应当保证数据的完整性。
GB2312
、
GBK
、
UTF-8
等都是可选的数据库字符集编码
;
当然我们也可以选择
ISO8859-1 (8-bit)
,只是我们得在应
用程序写数据之前先将
16Bit
的一个汉字或
Unicode
拆分成两个
8-bit
的字符,读数据之后也需要将两个字节合并起来,同时还要判别其中的
SBCS
字符,因此我们并不推荐采用
ISO8859-1
作为数据库字符集编码。这样不但没有充分利用数据库自身的字符集编码支持,而且同时也增加了编程的复杂度。编程时,可以先用数据库管理系统提供的管理功能检查其中的中文数据是否正确。
PHP
程序在查询数据库之前,首先执行
mysql_query("SET NAMES xxxx");
其中
xxxx
是你网页的编码
(charset=xxxx)
,如果网页中
charset=utf8
,则
xxxx=utf8
,如果网页中
charset=gb2312
,则
xxxx=gb2312
,几乎所有
WEB
程序,都有一段连接数据库的公共代码,放在一个文件里,在这文件里,加入
mysql_query("SET NAMES xxxx")
就可以了。
SET NAMES
显示客户端发送的
SQL
语句中使用什么字符集。因此,
SET NAMES 'utf-8'
语句告诉
服务器
“
将来从这个客户端传来的信息采用字符集
utf-8”
。它还为
服务
器发送回客户端的结果指定了字符集
(
例如,如果你使用一个
SELECT
语句,它表示列值使用了什么字符集
)
。
定位问题时常用的技巧
定位中文编码问题通常采用最笨的也是最有效的办法
―
在你认为有嫌疑的程序处理后打印字符串的内码。通过打印字符串的内码,你可以发现什么时候中文字符被转换成
Unicode
,什么时候
Unicode
被转回中文内码,什么时候一个中文字成了两个
Unicode
字符,什么时候中文字符串被转成了一串问号,什么时候中文字符串的高位被截掉了
……
取用合适的样本字符串也有助于区分问题的类型。如:
"aa
啊
[email=aa?@aa]
aa?@aa
[/email]"
等中英相间,
GB
、
GBK
特征字符均有的字符串。一般来说,英文字符无论怎么转换或处理,都不会失真
(
如果遇到了,可以尝试着增加连续的英文字母长度
)
。
解决各种应用的乱码问题
1)
使用
标签
设置页面编码
这个标签的作用是声明客户端的浏览器用什么字符集编码显示该页面,
xxx
可以为
GB2312
、
GBK
、
UTF-8(
和
MySQL
不同,
MySQL
是
UTF8)
等等。因此,大部分页面可以采用这种方式来告诉浏览器显示这个页面的时候采用什么编码,这样才不会造成编码错误而产生乱码。但是有的时候我们会发现有了这句还是不行,不管
xxx
是哪一种,浏览器采用的始终都是一种编码,这个情况我后面会谈到。
请注意,
是属于
HTML
信息的,仅仅是一个声明,仅表明服务器已经把
HTML
信息传到了浏览器。
2) header("content-type:text/html; charset=xxx");
这个函数
header()
的作用是把括号里面的信息发到
http
标头。如果括号里面的内容为文中所说那样,那作用和
标签基本相同,大家对照第一个看发现字符都差不多的。但是不同的是如果有这段函数,浏览器就会永远采用你所要求的
xxx
编码,绝对不会不听话,因此这个函数是很有用的。为什么会这样呢
?
那就得说说
http
标头和
HTML
信息的差别了:
http
标头是服务器以
http
协议传送
HTML
信息到浏览器前所送出的字串。而
标签是属于
HTML
信息的,所以
header()
发送的内容先到达浏览器,通俗点就是
header()
的优先级高于
(
不知道可不可以这样讲
)
。假如一个
php
页面既有
header("content-type:text/html;charset=xxx")
,又有,浏览器就只认前者
http
标头而不认
meta
了。当然这个函数只能在
php
页面内使用。
同样也留有一个问题,为什么前者就绝对起作用,而后者有时候就不行呢
?
这就是接下来要谈的
Apache
的原因了。
3) AddDefaultCharset
Apache
根目录的
conf
文件夹
里,有整个
Apache
的配置文档
httpd.conf
。
用文本编辑器打开
httpd.conf
,第
708
行
(
不同版本可能不同
)
有
AddDefaultCharset xxx
,
xxx
为编码名称。这行代码的意思:设置整个服务器内的网页文件
http
标头里的字符集为你默认的
xxx
字符集。有这行,就相当于给每个文件都加了一行
header("content-type:text/html; charset=xxx")
。这下就明白为什么明明
设置了是
utf-8
,可浏览器始终采用
gb2312
的原因。
如果网页里有
header("content-type:text/html; charset=xxx")
,就把默认的字符集改为你设置的字符集,所以这个函数永远有用。如果把
AddDefaultCharset xxx
前面加个
"#"
,注释掉这句,而且页面里不含
header("content-type…")
,那这个时候就轮到
meta
标签起作用了。
下面列出以上的优先顺序:
.. header("content-type:text/html; charset=xxx")
.. AddDefaultCharset xxx
..
如果你是
web
程序员,建议给你的每个页面都加个
header("content-type:text/html;charset=xxx")
,这样就可以保证它在任何服务器都能正确显示,可移植性也比较强。
4) php.ini
中的
default_charset
配置:
php.ini
中的
default_charset = "gb2312"
定义了
php
的默认语言字符集。一般推荐注释掉此行,让浏览器根据网页头中的
charset
来自动选择语言而非做一个强制性的规定,这样就可以在同台服务器上提供多种语言的网页服务。
结束语
其实
php
开发中的中文编码并没有想像的那么复杂,虽然定位和解决问题没有定规,各种运行环境也各不尽然,但后面的原理是一样的。了解字符集的知识是解决字符问题的基础。不过,随着中文字符集的变化,不仅仅是
php
编程,中文信息处理中的问题还是会存在一段时间的。
来源于北京教育招生网
欢迎光临 北京大学论坛 (http://pku.myubbs.com/)
Powered by Discuz! X3.3