飘灵儿 发表于 2015-2-4 00:02:25

PHP教程之用PHP对GB编码到UTF-8编码的静态转换

学习了六个多月PHP了,还是个新手,在这里受到了很多人的帮助,谢谢你们!   用ip2addr函数直接读取IP数据库文件是效力最高的,比拟用MySQL数据库存储IP数据,用SQL查询是效力最低的。然而IP数据库文件QQWry.dat是GB2312编码的。如今我需求UTF-8编码的地舆地位了局。假如用MySQL办法,可以在数据存入数据库时就转换为UTF-8编码,与日俱增。然而QQWry.dat文件又没法修正,只能把ip2addr函数的输入了局再停止静态转换。

静态转换GB->UTF-8编码最少有四种办法:

用PHP的iconv扩大转换

用PHP的mb_string扩大转换

用对调表转换,对调表存储在MySQL数据库中

用对调表转换,对调表存储在文本文件中

前两种办法要办事器作了响应设置(编译装置了响应扩大)才干利用。我的虚拟主机没有这两个扩大,只好思索后两种办法。前两个办法本文也不停止测评。

测评法式以下(func_ip.php拜见《IP地址->地舆地位转换的测评》一文):
<?php
require_once ("func_ip.php");
function u2utf8($c) {
$str = "";
if ($c < 0x80) {
    $str .= $c;
} elseif ($c < 0x800) {
    $str .= chr(0xC0 | $c >> 6);
    $str .= chr(0x80 | $c & 0x3F);
} elseif ($c < 0x10000) {
    $str .= chr(0xE0 | $c >> 12);
    $str .= chr(0x80 | $c >> 6 & 0x3F);
    $str .= chr(0x80 | $c & 0x3F);
} elseif ($c < 0x200000) {
    $str .= chr(0xF0 | $c >> 18);
    $str .= chr(0x80 | $c >> 12 & 0x3F);
    $str .= chr(0x80 | $c >> 6 & 0x3F);
    $str .= chr(0x80 | $c & 0x3F);
}
return $str;
}
function GB2UTF8_SQL($strGB) {
if (!trim($strGB)) return $strGB;
$strRet = "";
$intLen = strlen($strGB);
for ($i = 0; $i < $intLen; $i++) {
    if (ord($strGB{$i}) > 127) {
      $strCurr = substr($strGB, $i, 2);
      $intGB = hexdec(bin2hex($strCurr)) - 0x8080;
      $strSql = "SELECT code_unicode FROM nnstats_gb_unicode
          WHERE code_gb = ".$intGB." LIMIT 1"
      ;
      $resResult = mysql_query($strSql);
      if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
      else $strRet .= "??";
      $i++;
    } else {
      $strRet .= $strGB{$i};
    }
}
return $strRet;
}
function GB2UTF8_FILE($strGB) {
if (!trim($strGB)) return $strGB;
$arrLines = file("gb_unicode.txt");
foreach ($arrLines as $strLine) {
    $arrCodeTable = hexdec(substr($strLine, 7, 6));
}
$strRet = "";
$intLen = strlen($strGB);
for ($i = 0; $i < $intLen; $i++) {
    if (ord($strGB{$i}) > 127) {
      $strCurr = substr($strGB, $i, 2);
      $intGB = hexdec(bin2hex($strCurr)) - 0x8080;
      if ($arrCodeTable[$intGB]) $strRet .= u2utf8($arrCodeTable[$intGB]);
      else $strRet .= "??";
      $i++;
    } else {
      $strRet .= $strGB{$i};
    }
}
return $strRet;
}
function EncodeIp($strDotquadIp) {
$arrIpSep = explode('.', $strDotquadIp);
if (count($arrIpSep) != 4) return 0;
$intIp = 0;   
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
return $intIp;
//return sprintf('%02x%02x%02x%02x', $arrIpSep, $arrIpSep, $arrIpSep, $arrIpSep);
}
function GetMicroTime() {
list($msec, $sec) = explode(" ", microtime());
return ((double)$msec + (double)$sec);
}
for ($i = 0; $i < 100; $i++) { // 随机发生100个ip地址
$strIp = mt_rand(0, 255).".".mt_rand(0, 255).".".mt_rand(0, 255).".".mt_rand(0, 255);
$arrAddr[$i] = ip2addr(EncodeIp($strIp));
}
$resConn = mysql_connect("localhost", "netnest", "netnest");
mysql_select_db("test");
// 测评MySQL查询的编码转换
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// 测评停止并输入了局
echo $dblTimeDuration; echo "\r\n";
// 测评文本文件查询的编码转换
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// 测评停止并输入了局
echo $dblTimeDuration; echo "\r\n";
?>
测评两次了局(准确到3位小数,单元是秒):

MySQL查询转换:0.112
文本查询转换:10.590

MySQL查询转换:0.099
文本查询转换:10.623

可见此次是MySQL办法遥遥抢先于文件查询法。然而如今还不急于利用MySQL办法,由于文本文件办法之所以如斯耗时,次要由于它每次转换都要把全部gb_unicode.txt读入内存,而gb_unicode.txt又是文本文件,格局以下:

0x2121   0x3000   # IDEOGRAPHIC SPACE
0x2122   0x3001   # IDEOGRAPHIC COMMA
0x2123   0x3002   # IDEOGRAPHIC FULL STOP
0x2124   0x30FB   # KATAKANA MIDDLE DOT
0x2125   0x02C9   # MODIFIER LETTER MACRON (Mandarin Chinese first tone)
……
0x552A   0x6458   # <CJK>
0x552B   0x658B   # <CJK>
0x552C   0x5B85   # <CJK>
0x552D   0x7A84   # <CJK>
……
0x777B   0x9F37   # <CJK>
0x777C   0x9F3D   # <CJK>
0x777D   0x9F3E   # <CJK>
0x777E   0x9F44   # <CJK>

文本文件效力较低,因而思索把文本文件转换为二进制文件,然后用折半法查找这个文件,而不需求把全部文件读入内存。文件格局为:文件头2字节,存储纪录数;接着一条接一笔记录存入文件,每笔记录4字节,前2字节对应GB代码,后2字节对应Unicode代码。转换法式以下:
<?php
$arrLines = file("gb_unicode.txt");
foreach ($arrLines as $strLine) {
$arrCodeTable = hexdec(substr($strLine, 7, 6));
}
ksort($arrCodeTable);
$intCount = count($arrCodeTable);
$strCount = chr($intCount % 256) . chr(floor($intCount / 256));
$fileGBU = fopen("gbu.dat", "wb");
fwrite($fileGBU, $strCount);
foreach ($arrCodeTable as $k => $v) {
$strData = chr($k % 256) . chr(floor($k / 256)) . chr($v % 256) . chr(floor($v / 256));
fwrite($fileGBU, $strData);
}
fclose($fileGBU);
?>
履行法式后就取得了二进制的GB->Unicode对比表gbu.dat,而且数据纪录按GB代码排了序,便于折半法查找。利用gbu.dat停止转码的函数以下:
function GB2UTF8_FILE1($strGB) {
if (!trim($strGB)) return $strGB;
$fileGBU = fopen("gbu.dat", "rb");
$strBuf = fread($fileGBU, 2);
$intCount = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet = "";
$intLen = strlen($strGB);
for ($i = 0; $i < $intLen; $i++) {
    if (ord($strGB{$i}) > 127) {
      $strCurr = substr($strGB, $i, 2);
      $intGB = hexdec(bin2hex($strCurr)) - 0x8080;
      $intStart = 1;
      $intEnd = $intCount;
      while ($intStart < $intEnd - 1) { // 折半法查找
          $intMid = floor(($intStart + $intEnd) / 2);
          $intOffset = 2 + 4 * ($intMid - 1);
          fseek($fileGBU, $intOffset);
          $strBuf = fread($fileGBU, 2);
          $intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
          if ($intGB == $intCode) {
            $intStart = $intMid;
            break;
          }
          if ($intGB > $intCode) $intStart = $intMid;
          else $intEnd = $intMid;
      }
      $intOffset = 2 + 4 * ($intStart - 1);
      fseek($fileGBU, $intOffset);
      $strBuf = fread($fileGBU, 2);
      $intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
      if ($intGB == $intCode) {
          $strBuf = fread($fileGBU, 2);
          $intCodeU = ord($strBuf{0}) + 256 * ord($strBuf{1});
          $strRet .= u2utf8($intCodeU);
      } else {
          $strRet .= "??";
      }
      $i++;
    } else {
      $strRet .= $strGB{$i};
    }
}
return $strRet;
}
把其加到本来的测评法式,对三种办法同时测评2次失掉数据(准确到3位小数,单元:秒):

MySQL办法:0.125
文本文件办法:10.873
二进制文件折半法:0.106

MySQL办法:0.102
文本文件办法:10.677
二进制文件折半法:0.092

可见二进制文件折半法还比MySQL法略有优势。然而上述测评都是对短的地舆地位停止转码,假如对较长的文本转码又若何呢?我找来5个Blog的RSS 2.0文件,都是GB2312编码。测评三种办法对5个文件编码消耗的工夫,2次丈量数据以下(准确到3位小数,单元:秒):

MySQL办法:7.206
文本文件办法:0.772
二进制文件折半法:5.022

MySQL办法:7.440
文本文件办法:0.766
二进制文件折半法:5.055

可见对长的文本是用文本文件的办法最优,由于转码对比表读入内存后,转码就能够很高效了。既然如斯,咱们还可以测验考试改善一下,把文本文件办法改成:转码对比表从二进制文件gbu.dat读入内存,而不是文本文件。测评数据以下(精度和单元同上):

从文本文件读入对比表:0.766
从二进制文件读入对比表:0.831

从文本文件读入对比表:0.774
从二进制文件读入对比表:0.833

标明此次改善掉败了,从文本文件读入转码对比表更高效。

总结:用PHP对GB编码到UTF-8编码的静态转换,假如每次转换的文本很小,合适用二进制文件联合折半法转换;假如每次转换的文本较大,合适用文本文件存储转码对比表,并在转换前一次性把对比表读入内存。
那么接下来,这就算学会啦?NO,NO,NO,还早呢,你至尽还没碰过OOP之类的吧?模板呢?

活着的死人 发表于 2015-2-4 08:03:49

使用zendstdio 写代码的的时候,把tab 的缩进设置成4个空格是很有必要的

莫相离 发表于 2015-2-4 20:20:18

做为1门年轻的语言,php一直很努力。

透明 发表于 2015-2-9 10:20:22

写js我最烦的就是 ie 和 firefox下同样的代码 结果显示的结果千差万别,还是就是最好不要用遨游去调试,因为有时候遨游是禁用js的,有可能代码是争取结果被遨游折腾的认为是代码写错。

金色的骷髅 发表于 2015-2-27 05:37:36

因为blog这样的可以让你接触更多要学的知识,可以接触用到类,模板,js ,ajax

飘飘悠悠 发表于 2015-3-7 11:19:52

对于懒惰的朋友,我推荐php的集成环境xampp或者是wamp。这两个软件安装方便,使用简单。但是我还是强烈建议自己动手搭建开发环境。

分手快乐 发表于 2015-3-13 01:08:19

如果你已经到这种程度了,那么你已经可以做我的老师了。其实php也分很多的区域,

愤怒的大鸟 发表于 2015-3-16 10:34:44

为了以后维护的方便最好是代码上都加上注释,“予人方便,自己方便”。此外开发文档什么的最好都弄齐全。我觉得这是程序员必备的素质。虽然会消耗点很多的时间。但是确实是非常有必要的。

不帅 发表于 2015-3-16 13:23:46

使用 jquery 等js框架的时候,要随时注意浏览器的更新情况,不然很容易发生框架不能使用。

蒙在股里 发表于 2015-3-22 23:08:26

首推的搜索引擎当然是Google大神,其次我比较喜欢 百度知道。不过搜出来的结果往往都是 大家copy来copy去的,运气的的概率很大。

变相怪杰 发表于 2015-3-26 11:53:06

为了以后维护的方便最好是代码上都加上注释,“予人方便,自己方便”。此外开发文档什么的最好都弄齐全。我觉得这是程序员必备的素质。虽然会消耗点很多的时间。但是确实是非常有必要的。

小女巫 发表于 2015-4-12 07:04:09

做为1门年轻的语言,php一直很努力。

海妖 发表于 2015-4-15 07:27:01

学好程序语言,多些才是王道,写两个小时代码的作用绝对超过看一天书,这个我是深有体会(顺便还能练打字速度)。

简单生活 发表于 2015-4-20 21:19:13

开发工具也会慢慢的更专业,每个公司的可能不一样,但是zend studio是个大伙都会用的。

第二个灵魂 发表于 2015-4-21 18:13:43

在我安装pear包的时候老是提示,缺少某某文件,才发现 那群extension 的排列是应该有一点的顺序,而我安装的版本的排序不是正常的排序。没办法我只好把那群冒号加了上去,只留下我需要使用的扩展。

若天明 发表于 2015-4-23 20:38:48

有时候汉字的空格也能导致页面出错,所以在写代码的时候,要输入空格最好用引文模式。

飘灵儿 发表于 2015-4-26 21:11:58

写的比较杂,因为我也是个新手,不当至于大家多多指正。

若相依 发表于 2015-5-1 10:10:44

在我安装pear包的时候老是提示,缺少某某文件,才发现 那群extension 的排列是应该有一点的顺序,而我安装的版本的排序不是正常的排序。没办法我只好把那群冒号加了上去,只留下我需要使用的扩展。

仓酷云 发表于 2015-5-7 14:33:51

我要在声明一下:我是个菜鸟!!我对php这门优秀的语言也是知之甚少。但是我要在这里说一下php在网站开发中最常用的几个功能:
页: [1]
查看完整版本: PHP教程之用PHP对GB编码到UTF-8编码的静态转换