详谈PHP编码转换问题


Posted in PHP onJuly 28, 2015

最近恰好要用到unicode编码的转换,就去查了一下php的库函数,居然没找到一个函数可以对字符串进行Unicode的编码和解码!也罢,找不到的话就自己实现一下了。。。

Unicode和Utf-8编码的区别

Unicode是一个字符集,而UTF-8是Unicode的其中一种,Unicode是定长的都为双字节,而UTF-8是可变的,对于汉字来说Unicode占有的字节比UTF-8占用的字节少1个字节。Unicode为双字节,而UTF-8中汉字占三个字节。

UTF-8编码字符理论上可以最多到6个字节长,然而16位BMP(Basic Multilingual Plane)字符最多只用到3字节长。下面看一下

UTF-8编码表:

U-00000000 - U-0000007F: 0xxxxxxx 
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

xxx 的位置由字符编码数的二进制表示的位填入, 越靠右的 x 具有越少的特殊意义,只用最短的那个足够表达一个字符编码数的多字节串。 注意在多字节串中, 第一个字节的开头"1"的数目就是整个串中字节的数目。而第一行中以0开头,是为了兼容ASCII编码,为一个字节,第二行就为双字节字符串,第三行为3字节,如汉字就属于这种,以此类推。(个人认为:其实我们可以简单的把前面的1的个数看成字节数)

Unicode怎么转换成Utf-8呢

为了要将Unicode转换为UTF-8,当然要知道他们的区别到底在什么地方。下面来看一下,在Unicode中的编码是怎样转换成UTF-8的,在UTF-8中,如果一个字符的字节小于0x80(128)则为ASCII字符,占一个字节,可以不用转换,因为UTF-8兼容ASCII编码。假如在Unicode中汉字“你”的编码为“u4F60”,把它转换为二进制为100111101100000,然后按照UTF-8的方法进行转换。可以将Unicode二进制从低位往高位取出二进制数字,每次取6位,如上述的二进制就可以分别取出为如下所示的格式,前面按格式填补,不足8位用0填补。

unicode: 100111101100000                   4F60
utf-8:    11100100,10111101,10100000       E4BDA0

从上面就可以很直观的看出Unicode到UTF-8之间的转换,当然知道了UTF-8的格式后,就可以进行逆运算,就是按照格式把它在二进制中的相应位置上取出,然后在转换就是所得到的Unicode字符了(这个运算可以通过“位移”来完成)。如上述的“你”的转换,由于其值大于0x800小于0x10000,因此可以判断为三字节存储,则最高位需要向右移“12”位再根据三字节格式的最高位为11100000(0xE0)求或(|)就可以得到最高位的值了。同理第二位则是右移“6”位,则还剩下最高位和第二位的二进制值,可以通过与111111(0x3F)求按位于(&)操作,再和11000000(0x80)求或(|)。第三位就不用移位了,只要直接取最后六位(与111111(ox3F)取&),在与11000000(0x80)求或(|)。

Utf-8怎么逆转回Unicode呢

当然在UTF-8到Unicode的转换也是通过移位等来完成的,就是把UTF-8那些格式相应的位置的二进制数给揪出来。在上述例子中“你”为三个字节,因此要每个字节进行处理,有高位到低位进行处理。在UTF-8中“你”为11100100,10111101,10100000。从高位起即第一个字节11100100就是把其中的"0100"给取出来,这个很简单只要和11111(0x1F)取与(&),由三字节可以得知最到位肯定位于12位之前,因为每次取六位。所以还要将得到的结果左移12位,最高位也就这样完成了0100,000000,000000。而第二位则是要把“111101”给取出来,则只需将第二字节10111101和111111(0x3F)取与(&)。在将所得到的结果左移6位与最高字节所得的结果取或(|),第二位就这样完成了,得到的结果为0100,111101,000000。以此类推最后一位直接与111111(0x3F)取与(&),再与前面所得的结果取或(|)即可得到结果0100,111101,100000。

PHP代码实现

/**
 * utf8字符转换成Unicode字符
 * @param [type] $utf8_str Utf-8字符
 * @return [type]      Unicode字符
 */
function utf8_str_to_unicode($utf8_str) {
  $unicode = 0;
  $unicode = (ord($utf8_str[0]) & 0x1F) << 12;
  $unicode |= (ord($utf8_str[1]) & 0x3F) << 6;
  $unicode |= (ord($utf8_str[2]) & 0x3F);
  return dechex($unicode);
}

/**
 * Unicode字符转换成utf8字符
 * @param [type] $unicode_str Unicode字符
 * @return [type]       Utf-8字符
 */
function unicode_to_utf8($unicode_str) {
  $utf8_str = '';
  $code = intval(hexdec($unicode_str));
  //这里注意转换出来的code一定得是整形,这样才会正确的按位操作
  $ord_1 = decbin(0xe0 | ($code >> 12));
  $ord_2 = decbin(0x80 | (($code >> 6) & 0x3f));
  $ord_3 = decbin(0x80 | ($code & 0x3f));
  $utf8_str = chr(bindec($ord_1)) . chr(bindec($ord_2)) . chr(bindec($ord_3));
  return $utf8_str;
}

测试一下了

$utf8_str = '我';

//这是汉字“你”的Unicode编码
$unicode_str = '4f6b';

//输出 6211
echo utf8_str_to_unicode($utf8_str) . "<br/>";

//输出汉字“你”
echo unicode_str_to_utf8($unicode_str);

以上这些转换是针对中文汉字【往大了说是非ASCII】的测试,因为如果是ASCII的话,转来转去都是一样的,也用不着费那么大工夫。

还有就是这两个函数只是简单的实现了一下,只支持单个字符【一个完整的utf8字符或是一个完整的Unicode字符】互相转换,大家如果明白了得话就可以尽情去扩展了。。。

以上所述就是本文的全部内容了,希望大家能够喜欢。

PHP 相关文章推荐
提升PHP执行速度全攻略(下)
Oct 09 PHP
PHP执行linux系统命令的常用函数使用说明
Apr 27 PHP
PHP与C#分别格式化文件大小的代码
May 14 PHP
PHP提示Notice: Undefined variable的解决办法
Nov 24 PHP
一个比较不错的PHP日历类分享
Nov 18 PHP
php实现高效获取图片尺寸的方法
Dec 12 PHP
THINKPHP2.0到3.0有哪些改进之处
Jan 04 PHP
CI框架的安全性分析
May 18 PHP
PHP中new static()与new self()的比较
Aug 19 PHP
PHP创建XML的方法示例【基于DOMDocument类及SimpleXMLElement类】
Sep 10 PHP
php实现快速对二维数组某一列进行组装的方法小结
Dec 04 PHP
PHP手机号码及邮箱正则表达式实例解析
Jul 11 PHP
php技术实现加载字体并保存成图片
Jul 27 #PHP
php实现向javascript传递数组的方法
Jul 27 #PHP
ThinkPHP模型详解
Jul 27 #PHP
ThinkPHP控制器详解
Jul 27 #PHP
ThinkPHP路由详解
Jul 27 #PHP
ThinkPHP安装和设置
Jul 27 #PHP
教你在PHPStorm中配置Xdebug
Jul 27 #PHP
You might like
php的控制语句
2006/10/09 PHP
有关PHP中MVC的开发经验分享
2012/05/17 PHP
PHP函数实现分页含文本分页和数字分页
2014/10/23 PHP
自编函数解决pathinfo()函数处理中文问题
2014/11/03 PHP
PHP Warning: Module 'modulename' already loaded in问题解决办法
2015/03/16 PHP
nginx下安装php7+php5
2016/07/31 PHP
PHP实现超简单的SSL加密解密、验证及签名的方法示例
2017/08/28 PHP
phpmyadmin在宝塔面板里进不去的解决方案
2020/07/06 PHP
JavaScript调用堆栈及setTimeout使用方法深入剖析
2013/02/16 Javascript
jQuery图片滚动图片的效果(另类实现)
2013/06/02 Javascript
解决jquery1.9不支持browser对象的问题
2013/11/13 Javascript
巧用js提交表单轻松解决一个页面有多个提交按钮
2013/11/17 Javascript
JavaScript中实现异步编程模式的4种方法
2014/09/24 Javascript
jQuery复制表单元素附源码分享效果演示
2015/09/30 Javascript
果断收藏9个Javascript代码高亮脚本
2016/01/06 Javascript
全面解析Bootstrap布局组件应用
2016/02/22 Javascript
微信小程序 图片宽度自适应的实现
2017/04/06 Javascript
JavaScript实现无刷新上传预览图片功能
2017/08/02 Javascript
Vue封装一个简单轻量的上传文件组件的示例
2018/03/21 Javascript
JS实现区分中英文并统计字符个数的方法示例
2018/06/09 Javascript
浅谈VUE单页应用首屏加载速度优化方案
2018/08/28 Javascript
引入外部js脚本加载慢与页面白屏问题的解决
2018/12/10 Javascript
性能优化篇之Webpack构建速度优化的建议
2019/04/03 Javascript
JS中的算法与数据结构之二叉查找树(Binary Sort Tree)实例详解
2019/08/16 Javascript
jQuery 动画与停止动画效果实例详解
2020/05/19 jQuery
JavaScript实现猜数字游戏
2020/05/20 Javascript
理解Python中的With语句
2016/03/18 Python
python TCP Socket的粘包和分包的处理详解
2018/02/09 Python
python中不能连接超时的问题及解决方法
2018/06/10 Python
python实现简单图书管理系统
2019/11/22 Python
Python中实现一行拆多行和多行并一行的示例代码
2020/09/06 Python
利用HTML5中Geolocation获取地理位置调用Google Map API在Google Map上定位
2013/01/23 HTML / CSS
New Balance加拿大官方网站:运动鞋和健身服装
2018/11/19 全球购物
澳大利亚人信任的清洁平台,您的私人管家:Jarvis
2020/12/25 全球购物
大学自我评价
2014/02/12 职场文书
关于爱国的演讲稿
2014/05/07 职场文书