記事「IP アドレス -> 地理的位置変換の評価」では、SQL クエリを使用して 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 つの方法を検討する必要があります。最初の 2 つの方法は、この記事では評価されません。
测评程序如下(func_ip.php参见《IP地址->地理位置转换的测评》一文):
2つの評価結果(小数点以下3桁までの精度、単位は秒):
MySQLクエリ変換: 0.112
テキストクエリ変換: 10.590
MySQLクエリ変換: 0.099
テキストクエリ変換: 10.623
ということがわかります。今回は、MySQL メソッドはファイル クエリメソッドよりもはるかに進んでいます。しかし、今すぐに MySQL 方式を急いで使用する必要はありません。テキスト ファイル方式は主に、変換のたびに gb_unicode.txt 全体をメモリに読み込む必要があるため、非常に時間がかかります。また、gb_unicode.txt は次のようなテキスト ファイルです。次の形式:
テキスト ファイルは非効率なので、テキスト ファイルをバイナリ ファイルに変換し、ファイル全体をメモリに読み込まずに中途半端な方法でファイルを見つけることを検討しました。ファイル形式は次のとおりです。ファイル ヘッダーは 2 バイトで、レコード数が格納されます。その後、レコードが 1 つずつファイルに格納されます。各レコードは 4 バイトで、最初の 2 バイトは GB コードに対応し、最後の 2 バイトは GB コードに対応します。 Unicodeコードに対応しています。変換手順は以下の通りです
プログラムを実行すると、バイナリ GB->Unicode 比較テーブル gbu.dat が取得され、データ レコードが GB コードでソートされ、ハーフメソッドによる検索に便利です。 gbu.dat を使用してトランスコードする関数は次のとおりです:
GB->UTF-8 エンコーディングを動的に変換するには、少なくとも 4 つの方法があります:
PHP の iconv 拡張機能を使用した変換
PHP の mb_string 拡張機能を使用した変換
MySQL に保存されているスワップ テーブルを使用した変換 データベース最初の 2 つの方法は、テキスト ファイルに保存されるスワップ テーブルを使用して変換されます。最初の 2 つの方法を使用するには、サーバー上で対応する設定 (対応する拡張機能のコンパイルとインストール) が必要です。私の仮想ホストにはこれら 2 つの拡張機能がないため、後者の 2 つの方法を検討する必要があります。最初の 2 つの方法は、この記事では評価されません。
测评程序如下(func_ip.php参见《IP地址->地理位置转换的测评》一文):
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, 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]);
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[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
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 " ";
// 测评文本文件查询的编码转换
$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 " ";
?>
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, 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]);
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[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
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 " ";
// 测评文本文件查询的编码转换
$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 " ";
?>
2つの評価結果(小数点以下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 #カタカナ中点
0x2125 0x02C9 #修飾文字 MACRON (中国語の最初の声調)
……
0x552A 0x6458 #
0x552B 0x658B #
0x552C 0x5B85 #
0x552D 0x7A84 #
……
0x777B 0x9F37 #
0x777 C 0x9F3D #
0x777D 0x9F3E # ;
0x777E 0x9F44 #
0x2122 0x3001 #IDEOGRAPHIC COMMA
0x2123 0x3002 #IDEOGRAPHIC FULL STOP
0x2124 0x30FB #カタカナ中点
0x2125 0x02C9 #修飾文字 MACRON (中国語の最初の声調)
……
0x552A 0x6458 #
0x552B 0x658B #
0x552C 0x5B85 #
0x552D 0x7A84 #
……
0x777B 0x9F37 #
0x777 C 0x9F3D #
0x777D 0x9F3E #
0x777E 0x9F44 #
テキスト ファイルは非効率なので、テキスト ファイルをバイナリ ファイルに変換し、ファイル全体をメモリに読み込まずに中途半端な方法でファイルを見つけることを検討しました。ファイル形式は次のとおりです。ファイル ヘッダーは 2 バイトで、レコード数が格納されます。その後、レコードが 1 つずつファイルに格納されます。各レコードは 4 バイトで、最初の 2 バイトは GB コードに対応し、最後の 2 バイトは GB コードに対応します。 Unicodeコードに対応しています。変換手順は以下の通りです
$arrLines = file("gb_unicode.txt");
foreach ($arrLines as $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = 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($v % 256) ;
fwrite($fileGBU, $strData);
}
fclose( $fileGBU);
?>
foreach ($arrLines as $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = 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($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 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
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 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