"IP 주소 평가->지리적 위치 변환" 기사에서는 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");
함수 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)) 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 = "nnstats_gb_unicode에서 code_unicode 선택
WHERE code_gb = ".$intGB."
;
$resResult = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
else $strRet .= "??";
$i++;
} 또 다른 {
$strRet .= $strGB{$i};
}
}
$strRet를 반환합니다.
}
함수 GB2UTF8_FILE($strGB) {
if (!trim($strGB)) return $strGB;
$arrLines = file("gb_unicode.txt");
foreach ($arrLines를 $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++;
} 또 다른 {
$strRet .= $strGB{$i};
}
}
$strRet를 반환합니다.
}
함수 EncodeIp($strDotquadIp) {
$arrIpSep = 폭발('.', $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) = 폭발(" ", 마이크로타임());
return ((더블)$msec + (더블)$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]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//평가가 종료되고 결과가 출력됩니다.
에코 $dblTimeDuration 에코 "rn";
// 평가 텍스트 파일 쿼리의 인코딩 변환
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["지역"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//평가가 종료되고 결과가 출력됩니다.
에코 $dblTimeDuration 에코 "rn";
?>
두 가지 평가 결과(소수점 3자리까지 정확, 단위는 초):
MySQL 쿼리 변환: 0.112
텍스트 쿼리 변환: 10.590
MySQL 쿼리 변환: 0.099
텍스트 쿼리 변환: 10.623
이번에는 MySQL 방식이 파일 쿼리 방식보다 훨씬 앞선다는 것을 알 수 있습니다. 그러나 이제는 MySQL 방법을 사용하기 위해 서두르지 않습니다. 왜냐하면 텍스트 파일 방법은 주로 변환할 때마다 전체 gb_unicode.txt를 메모리로 읽어야 하고 gb_unicode.txt는 다음 형식:
0x2121 0x3000 #IDEOGRAPHICSPACE
0x2122 0x3001 #표의적 쉼표
0x2123 0x3002 #표의적 마침표
0x2124 0x30FB # 가타카나 가운데 점
0x2125 0x02C9 # MODIFIER LETTER 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바이트이며 레코드 수를 저장합니다. 레코드는 하나씩 파일에 저장됩니다. 각 레코드는 4바이트이며 처음 2바이트는 GB 코드에 해당하고 마지막 2바이트는 유니코드 코드에 해당합니다. 변환 절차는 다음과 같습니다.
<?php
$arrLines = file("gb_unicode.txt");
foreach ($arrLines를 $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은 $k => $v) {
$strData = chr($k % 256) . chr(floor($k / 256)) . chr($v % 256) .
fwrite($fileGBU, $strData);
}
fclose($fileGBU);
?>
프로그램을 실행한 후 바이너리 GB->유니코드 비교 테이블 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) { // 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자리까지 정확함, 단위: 초):
MySQL 방법: 0.125
텍스트 파일 방식: 10.873
바이너리 파일 반감기 방식: 0.106
MySQL 방식: 0.102
텍스트 파일 방식: 10.677
바이너리 파일 반감 방법: 0.092
바이너리 파일 반감 방법이 MySQL 방법에 비해 약간의 장점이 있음을 알 수 있습니다. 그러나 위의 평가는 모두 짧은 지리적 위치를 트랜스코딩합니다. 긴 텍스트를 트랜스코딩하면 어떻게 될까요? 모두 GB2312로 인코딩된 5개의 블로그 RSS 2.0 파일을 찾았습니다. 3가지 방법을 사용하여 5개의 파일을 인코딩하는 데 걸리는 시간을 평가합니다. 두 가지 측정 데이터는 다음과 같습니다. (정확도는 소수점 이하 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 인코딩으로 동적으로 변환합니다. 매번 변환되는 텍스트가 작은 경우에는 중간 변환 방법을 결합한 바이너리 파일을 사용하는 것이 적합합니다. 텍스트 파일을 사용하여 코드 변환 테이블을 저장하고 변환 전에 메모리로 한 번 읽어들이는 것이 적합합니다.