使php支持mbstring库

《PHP中的字符串、编码、UTF-8》一文中描述了一些列的基础知识,比较枯燥,现在来说点有用的——PHP
字符串处理的最佳实践,本文是“PHP、字符串、编码、UTF-8”相关知识的第二部分。先说结论——
PHP 中的各个方面使用 UTF-8
编码。

多国语言并存就意味着多字节,PHP内置的字符串长度函数strlen无法正确处理中文字符串,它得到的只是字符串所占的字节数。对于GB2312的中文编码,strlen得到的值是汉字个数的2倍,而对于UTF-8编码的中文,就是1~3倍的差异了。

没有一行式解决方案。小心、注意细节,以及一致性。

PHP 中的 UTF-8 糟透了。原谅我的用词。

目前 PHP 在低层次上还不支持 Unicode。有几种方式可以确保 UTF-8
字符串能够被正确处理, 但并不容易,需要深入到 web 应用的所有层面,从
HTML,到 SQL,到 PHP。我们旨在提供一个简洁、 实用的概述。

PHP 语言层面是不支持 Unicode字符集的,但是可以通过 UTF-8
编码能处理大部分问题。

   

PHP 层面的 UTF-8

基本的字符串操作,如串接
两个字符串、将字符串赋给变量,并不需要任何针对 UTF-8 的特殊东西。
然而,多数 字符串函数,如
strpos() 和
strlen,就需要特殊的考虑。
这些函数都有一个对应的 mb_*
函数:例如,mb_strpos()
和 mb_strlen()。
这些对应的函数统称为多字节字符串函数。
这些多字节字符串函数是专门为操作 Unicode 字符串而设计的。

当你操作 Unicode 字符串时,必须使用 mb_* 函数。 例如,如果你使用
substr() 操作一个 UTF-8
字符串,其结果就很可能包含一些乱码。 正确的函数应该是对应的多字节函数,
mb_substr()。

难的是始终记得使用 mb_* 函数。即使你仅一次忘了,你的 Unicode
字符串在接下来的处理中就可能产生乱码。

并不是所有的字符串函数都有一个对应的
mb_*。如果不存在你想要的那一个,那你就只能自认倒霉了。

此外,在每个 PHP 脚本的顶部(或者在全局包含脚本的顶部)你都应使用
mb_internal_encoding
函数,如果你的脚本会输出到浏览器,那么还得紧跟其后加个mb_http_output()
函数。在每个脚本中显式地定义字符串的编码在以后能为你减少很多令人头疼的事情。

最后,许多操作字符串的 PHP 函数都有一个可选参数让你指定字符编码。
若有该选项, 你应始终显式地指明 UTF-8 编码。
例如,htmlentities()
就有一个字符编码方式选项,在处理这样的字符串时应始终指定 UTF-8。

最佳实践就是明确知道输入编码(不知道就检测),内部统一转换为 UTF-8
编码,输出编码也统一是 UTF-8编码。

 

MySQL 层面的 UTF-8

如果你的 PHP 脚本会访问
MySQL,即使你遵从了前述的注意事项,你的字符串也有可能在数据库中存储为非
UTF-8 字符串。

确保从 PHP 到 MySQL 的字符串为 UTF-8
编码的,确保你的数据库以及数据表均设置为 utf8mb4 字符集,
并且在你的数据库中执行任何其他查询之前先执行 MySQL 查询 `set names
utf8mb4`。这是至关重要的。 示例请查看连接并查询 MySQL
数据库一节内容。

注意你必须使用 `utf8mb4` 字符集来获得完整的 UTF-8 支持,而不是
`utf8`
字符集!原因请查看进一步阅读。

PHP 层面如何处理 UTF-8

当操作 Unicode 字符集的时候,请务必安装 mbstring
扩展,并使用相应的函数代替原生的字符串函数。举个例子,一个文件编码为
UTF-8 的 PHP 代码,假如使用 strlen() 函数是错误的,请使用 mb_strlen()
函数代替。

mbstring
扩展大部分的函数都需要基于一个编码(内部编码)来处理,请务必统一使用
UTF-8 编码,这个大部分可以在 PHP.INI 中配置。

从 PHP 5.6 开始,default_charset 配置可以替换
mbstring.http_澳门新浦京电子游戏,input,mbstring.http_output 。
另外一个重要的配置就是 mbstring.language,这个默认值是
Neutral(UTF-8)。

注意文件编码和 mbstring 扩展的内部编码不是同一个概念。

概括的说来:

  • PHP.INI 中涉及到 mbstring 扩展的部分尽量使用 UTF-8。
  • 请用 mbstring 扩展函数代替原生字符串操作函数。
  • 在使用相关函数的时候,请务必了解你操作的字符的编码是什么,在使用对应函数的时候,显示的写上
    UTF-8 编码参数,比如 htmlentities() 函数的第三个参数显示写上 UTF-8。

   

浏览器层面的 UTF-8

使用
mb_http_output()
函数 来确保你的 PHP 脚本输出 UTF-8 字符串到浏览器。 并且在 HTML 页面的
<head> 标签块中包含 字符集 <meta>
标签块。

文件 IO 操作 如何处理 UTF-8

这里举个例子,假如你要打开一个文件,但是不知道文件内容是什么编码的,那么如何处理呢?

最佳实践就是,在打开的时候统一转换成
UTF-8,修改内容后就再转回原来的编码并保存到文件。看代码吧:

if ( mb_internal_encoding()!="UTF-8") {
        mb_internal_encoding("UTF-8");
}

$file = "file.txt"; //一个编码为gbk的中文文件
$str= file_get_contents($file);
//不管来源是什么编码,统一显示的时候转换为 UTF-8
 if (mb_check_encoding($str,"GBK")) 
    $str =  mb_convert_encoding($str,"UTF-8",“GBK”); 

$str ="修改内容";
$str =  mb_convert_encoding($str,$srcbm,"UTF-8"); //原样转回去
file_put_contents($file,$str);

采用PHP字符串mbstring可以较好地解决这个问题。mb_strlen的用法和strlen类似,只不过它有第二个可选参数用于指定字符编码。例如得到UTF-8的字符串$str长度,可以用mb_strlen($str,’UTF-8′)。如果省略第二个参数,则会使用PHP的内部编码。内部编码可以通过mb_internal_encoding()函数得到,设置有两种方式:

示例

<?php
// Tell PHP that we're using UTF-8 strings until the end of the script
mb_internal_encoding('UTF-8');

// Tell PHP that we'll be outputting UTF-8 to the browser
mb_http_output('UTF-8');

// Our UTF-8 test string
$string = 'Aš galiu valgyti stiklą ir jis manęs nežeidžia';

// Transform the string in some way with a multibyte function
$string = mb_substr($string, 0, 10);

// Connect to a database to store the transformed string
// See the PDO example in this document for more information
// Note the `set names utf8mb4` commmand!
$link = new PDO(   'mysql:host=your-hostname;dbname=your-db',
                    'your-username',
                    'your-password',
                    array(
                        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                        PDO::ATTR_PERSISTENT => false,
                        PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4'
                    )
                );

// Store our transformed string as UTF-8 in our database
// Assume our DB and tables are in the utf8mb4 character set and collation
$handle = $link->prepare('insert into Sentences (Id, Body) values (?, ?)');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->bindValue(2, $string);
$handle->execute();

// Retrieve the string we just stored to prove it was stored correctly
$handle = $link->prepare('select * from Sentences where Id = ?');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->execute();

// Store the result into an object that we'll output later in our HTML
$result = $handle->fetchAll(PDO::FETCH_OBJ);
?><!doctype html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>UTF-8 test page</title>
    </head>
    <body>
        <?php
        foreach($result as $row){
            print($row->Body);  // This should correctly output our transformed UTF-8 string to the browser
        }
        ?>
    </body>
</html>

 

Mysql 和 UTF-8 的最佳实践

这个相对简单,首先保证你的 Mysql 都是 UTF-8。然后 Mysql
客户端连接的时候也保持 UTF-8,具体到 PHP 中,就是 imysql 或者 PDO
扩展连接 Mysql 的时候都设置 UTF-8
作为连接编码,二边保持一致,一般就不会遇到问题。

有兴趣可以看看这篇文章

   

进一步阅读

  • PHP
    手册:多字节字符串函数
  • PHP UTF-8
    备忘单
  • Stack Overflow: 什么因素致使 PHP 不兼容
    Unicode?
  • Stack Overflow: PHP 与 MySQL
    之间国际化字符串的最佳实践
  • 怎样在MySQL数据库中完整支持Unicode

浏览器和 UTF-8 的最佳实践

这个也比较简单,就是你的输出内容假如是网页,那么你的字符串处理输出最总请保持为
UTF-8 ;同时 PHP.INI 中也明确设定 default_charset 为 UTF-8;HTML 的
Meta Tag 也明确标识为 UTF-8。

现在万事大吉了吗,并没有,虽然服务器和浏览器让用户使用 UTF-8
编码,但是用户的行为并没有约束性,他可能输入的是其他编码的字符,或者上传的文件名是其他编码的字符,那么怎么办呢?可以通过
mb_http_input() 和 mb_check_encoding()
函数来检测用户的编码,然后内部转换为
UTF-8。确保在任何一个层面,最终处理的是 UTF-8
编码。换句话说,需要手段能够知晓你的输入是什么编码的,处理完成后控制输出的编码是
UTF-8。

不建议使用 mbstring.encoding_translation 指令 和 mb_detect_encoding()
函数。折磨我半天。

1.  在php.ini中设置mbstring.internal_encoding  =  UTF-8<br  >   
2.  调用mb_internal_encoding(”GBK”)</br  >

操作系统和 UTF-8 的最佳实践

由于操作系统的原因,PHP 处理 Unicode 文件名的时候会有不同的处理机制。

在 Linux 中,文件名始终是 UTF-8 编码的,而在中文 Windows
环境下,文件名始终是 GBK 编码的,记住这一点就可以了。

通过例子说明下:

//命令行程序函数,运行在中文版 Windows 10 操作系统 ,文件编码为 UTF-8

function filenameexample() {
    $filename = "测试.txt" ;
    $gbk_filename = iconv("UTF-8","GBK",$filename);
    file_put_contents($gbk_filename, "测试");
    echo file_get_contents($gbk_filename);
}

function scandirexample() {
    $arr = scandir("./tmp");
    foreach ($arr as $v) {
        if ($v == "." || $v =="..")
            continue ;
        $filename = iconv( "GBK","UTF-8",$v ) ;
        $content = file_get_contents("./tmp/" . $v );
    }
}

假如不想写写兼容 Windows 和 linux 的程序,可以对文件名进行 urlencode
编码,比如:

 function urlencodeexample() {
    $filename = "测试2.txt" ;
    $urlencodefilename = urlencode($filename) ;
    file_put_contents($urlencodefilename, "测试");
    echo file_get_contents($urlencodefilename);
 }

在用 PHP 通过 header()
函数下载文件的时候,也要考虑浏览器和操作系统(大部分人使用的是
Windows),对于 Chrome 来说,输出的文件名编码可以是 UTF-8,Chrome
会自动将文件名转换为 GBK 编码。

而对于低版本的 IE
来说,它继承了操作系统的环境,所以下载文件名假如是中文必须转码为 UTF-8
编码,否则下载的时候用户看到的是乱码文件名。通过代码来说明:

$agent=$_SERVER["HTTP_USER_AGENT"];
if(strpos($agent,'MSIE')!==false  {
    $filename = iconv("UTF-8","GBK","附件.txt");
    header("Content-Disposition: attachment; filename="$filename"");
}

   

除了PHP字符串mbstring,还有很多切割函数,其中mb_substr是按字来切分字符,而mb_strcut是按字节来切分字符,但是都不会产生半个字符的现象。而且从函数切割对长度的作用也不同,mb_strcut的切割条件是小于strlen,  mb_substr是等于strlen,看下面的例子,

   
<ol  class=”dp-xml”><li  class=”alt”><span  class=”tag”><font  color=”#006699″><</font  color=”#006699″></span  class=”tag”> ?   

  • $<span  class=”attribute”><font  color=”#ff0000″>str = ‘我是一串比较长的中文-www.jefflei.com’;   </font  color=”#ff0000″></span  class=”attribute”>

<li  class=”alt”>echo “mb_substr:” . mb_substr($str, 0, 6, ‘utf-8′);   

  • echo ”   

<li  class=”alt”>“;   

  • echo “mb_strcut:” . mb_strcut($str, 0, 6, ‘utf-8′);   

<li  class=”alt”><span  class=”tag”><font  color=”#006699″>?></font  color=”#006699″> 

  •  

</span  class=”tag”></li  class=”alt”></li  class=”alt”></li  class=”alt”></li  class=”alt”></ol  class=”dp-xml”>   

输出如下:<br  >    mb_substr:我是一串比较<br  >   
mb_strcut:我是</br  ></br  >

   

需要注意的是,PHP字符串mbstring并不是PHP核心函数,使用前需要确保在php编译模块时加入mbstring的支持:<br  >   
(1)编译时使用–enable-mbstring<br  >   
(2)修改/usr/local/lib/php.inc<br  >   
default_charset  =  “zh-cn”<br  >   
mbstring.language  =  zh-cn<br  >   
mbstring.internal_encoding  =zh-cn</br  ></br  ></br  ></br  ></br  >

   

PHP字符串mbstring类库内容比较多,还包括mb_  send_  mail  之类的email处理函数等

 

 

 

 

 

 

配置mbstring扩展

首先确保php安装目录下的ext子目录存在php_mbstring.dll 文件。然后编辑php.ini文件:

          ;extension=php_mbstring.dll 去掉分号
            extension=php_mbstring.dll

   把输出处理程序设为mbstring对应。    ;output_handler =
   output_handler = “mb_output_handler”

   设定文字集为UTF-8。    ;default_charset = “iso-8859-1”
    default_charset = “UTF-8”

   设置mbstring的语言为Chinese    ;mbstring.language = Japanese
    mbstring.language = Chinese

   设置mbstring的内部编码为UTF-8。    ;mbstring.internal_encoding
= EUC-JP     mbstring.internal_encoding = UTF-8

   设置mbstring的输入文字编码为自动判别方式。
   ;mbstring.http_input = auto     mbstring.http_input =
auto

   把mbstring输出文字编码设为UTF-8。    ;mbstring.http_output =
SJIS    mbstring.http_output = UTF-8

   激活mbstring的文字编码自动变换方式。
   ;mbstring.encoding_translation = Off
    mbstring.encoding_translation = On

mbstring库
全称是Multi-Byte String
即各种语言都有自己的编码,他们的字节数是不一样的,目前php内部的编码只支持ISO-8859-*,
EUC-JP, UTF-8其他的编码的语言是没办法在php程序上正确显示的。
解决的方法就是通过php的mbstring函数库来解决其安装是在编译php的时候加上–enable-mbstring=?
“=”后面就是跟需要支持的语言,j具体参数如下:–enable-mbstring=cn
for Simplified Chinese support, –enable-mbstring=tw for Traditional
Chinese support, –enable-mbstring=kr for Korean support,
–enable-mbstring=ru for Russian support, and –enable-mbstring=ja for
Japanese support.“没有发现PHP
的扩展设置mbstring,而当前系统好像在使用宽字符集。没有mbstring
扩展的phpMyAdmin
不能正确识别字符串,可能产生不可意料的结果.”
这是因为我们在装PHP的时候没有把mbstring模块加上。
在win下很简单啦,只要到配置文件里把mbstring.dll前面的分号去掉,并把文件放到相应的地方就可以了,在linux下就不同了,但是也很简单。只要在编译PHP的时候加上参数
–with-mbstring
–enable-mbstring=all

就可以了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注