「IP アドレス -> 地理的位置変換の評価」では、MySQL データベースを使用して IP データを保存するのと比較して、ip2addr 関数を使用して IP データベース ファイルを直接読み取るのが最も効率的であると述べられています。最も効率が悪い。ただし、IP データベース ファイル QQWry.dat は GB2312 でエンコードされています。次に、UTF-8 でエンコードされた地理位置情報の結果が必要になります。 MySQL メソッドを使用すると、データをデータベースに保存するときに、データを UTF-8 エンコーディングに完全に変換できます。ただし、QQWry.dat ファイルは変更できず、ip2addr 関数の出力結果は動的変換のみ可能です。
GB->UTF-8 エンコーディングを動的に変換するには、少なくとも 4 つの方法があります。PHP
の iconv 拡張機能を使用して変換する、
PHP の mb_string 拡張機能を使用して変換する
、スワップ テーブル変換を使用する、およびスワップ テーブルはスワップ テーブル変換を使用して MySQL データベース
に
保存されます。 、スワップ テーブルテキスト ファイルに保存する
最初の 2 つの方法は、サーバーが適切に設定されている (対応する拡張機能がコンパイルおよびインストールされている) 場合にのみ使用できます。私の仮想ホストにはこれら 2 つの拡張機能がないため、後者の 2 つの方法を検討する必要があります。最初の 2 つの方法は、この記事では評価されません。
評価手順は次のとおりです (func_ip.php については、「IP アドレス-
> 地理的位置変換の評価」の記事を参照してください)。
require_once ("func_ip.php");
関数 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);
}
$str を返します。
}
関数 GB2UTF8_SQL($strGB) {
if (!trim($strGB)) $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 = "nnstats_gb_unicode から code_unicode を選択します
WHERE code_gb = ".$intGB."
;
$resResult = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
それ以外の場合 $strRet .= "??";
$i++;
} それ以外 {
$strRet .= $strGB{$i};
}
}
$strRet を返します。
}
関数 GB2UTF8_FILE($strGB) {
if (!trim($strGB)) $strGB を返します。
$arrLines = file("gb_unicode.txt");
foreach ($arrLines as $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = 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]);
それ以外の場合 $strRet .= "??";
$i++;
} それ以外 {
$strRet .= $strGB{$i};
}
}
$strRet を返します。
}
関数 EncodeIp($strDotquadIp) {
$arrIpSep =explode('.', $strDotquadIp);
if (count($arrIpSep) != 4) 0 を返します。
$intIp = 0;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
$intIp を返します。
//return sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
関数 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("テスト");
// MySQL クエリのエンコード変換を評価します
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["リージョン"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["アドレス"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//評価が終了し結果が出力される
エコー $dblTimeDuration; エコー "rn";
// 評価用テキストファイルクエリのエンコード変換
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["リージョン"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["アドレス"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//評価が終了し結果が出力される
エコー $dblTimeDuration; エコー "rn";
?>
2 つの評価の結果 (小数点以下 3 桁までの精度、単位は秒):
MySQL クエリ変換: 0.112
テキストクエリ変換: 10.590
MySQL クエリ変換: 0.099
テキスト クエリ変換: 10.623
今回は、MySQL 方式がファイル クエリ方式よりもはるかに優れていることがわかります。しかし、今すぐに MySQL 方式を急いで使用する必要はありません。テキスト ファイル方式は主に、変換のたびに gb_unicode.txt 全体をメモリに読み込む必要があるため、非常に時間がかかります。また、gb_unicode.txt は次のようなテキスト ファイルです。次の形式:
0x2121 0x3000 #IDEOGRAPHICSPACE
0x2122 0x3001 #IDEOGRAPHIC COMMA
0x2123 0x3002 #アイデアグラフィックフルストップ
0x2124 0x30FB # カタカナ中点
0x2125 0x02C9 # 修飾文字 MACRON (中国語の第一声調)
…
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 バイトで、レコード数が格納されます。その後、レコードが 1 つずつファイルに格納されます。各レコードは 4 バイトで、最初の 2 バイトは GB コードに対応し、最後の 2 バイトは GB コードに対応します。 Unicodeコードに対応しています。変換手順は以下のとおりです。
<?php
$arrLines = file("gb_unicode.txt");
foreach ($arrLines as $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
ksort($arrCodeTable);
$intCount = カウント($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(floor($v / 256));
fwrite($fileGBU, $strData);
}
fclose($fileGBU);
?>
プログラムを実行すると、バイナリ GB->Unicode 比較テーブル gbu.dat が取得され、GB コードに従ってデータ レコードがソートされます。これは、ハーフ法による検索に便利です。 gbu.dat を使用してトランスコードする関数は次のとおりです。
function GB2UTF8_FILE1($strGB) {
if (!trim($strGB)) $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) { //half メソッドで検索
$intMid = フロア(($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;
壊す;
}
if ($intGB > $intCode) $intStart = $intMid;
それ以外の場合は $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);
} それ以外 {
$strRet .= "??";
}
$i++;
} それ以外 {
$strRet .= $strGB{$i};
}
}
$strRet を返します。
}
これを元の評価プログラムに追加し、3 つのメソッドを同時に 2 回評価してデータを取得します (小数点以下 3 桁までの精度、単位: 秒):
MySQL メソッド: 0.125
テキストファイル方式:10.873
バイナリファイルの半分化方式: 0.106
MySQL 方式: 0.102
テキストファイル方式:10.677
バイナリ ファイルの半分化方法: 0.092
バイナリ ファイルの半分化方法は、MySQL 方法よりわずかに優れていることがわかります。ただし、上記の評価ではすべて短い地理的位置がトランスコードされますが、長いテキストがトランスコードされた場合はどうなるでしょうか。 5 つのブログ RSS 2.0 ファイルが見つかりました。すべて GB2312 でエンコードされています。 5 つのファイルをエンコードするのにかかる時間を 3 つの方法で評価した場合、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 エンコードに動的に変換します。毎回変換されるテキストが小さい場合は、バイナリ ファイルと途中変換方法を組み合わせて使用するのが適しています。テキスト ファイルを使用してトランスコーディングを保存し、変換前にそのルックアップ テーブルをメモリに読み込むのが適切です。