В статье «Оценка преобразования 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) {
$стр = "";
если ($c <0x80) {
$стр.= $с;
} 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);
}
вернуть $стр;
}
функция GB2UTF8_SQL($strGB) {
if (!trim($strGB)) вернуть $strGB;
$strRet = "";
$intLen = стрлен($strGB);
for ($i = 0; $i < $intLen; $i++) {
if (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
$strSql = "ВЫБРАТЬ code_unicode ИЗ nnstats_gb_unicode
ГДЕ code_gb = ".$intGB LIMIT 1"
;
$resResult = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
еще $strRet .= "??";
$я++;
} еще {
$strRet .= $strGB{$i};
}
}
вернуть $стррет;
}
функция GB2UTF8_FILE($strGB) {
if (!trim($strGB)) вернуть $strGB;
$arrLines = файл("gb_unicode.txt");
foreach ($arrLines как $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
$strRet = "";
$intLen = стрлен($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 .= "??";
$я++;
} еще {
$strRet .= $strGB{$i};
}
}
вернуть $стррет;
}
функция EncodeIp($strDotquadIp) {
$arrIpSep = взорваться('.', $strDotquadIp);
if (count($arrIpSep) != 4) вернуть 0;
$интИп = 0;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
вернуть $intIp;
// возвращаем sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
функция GetMicroTime() {
list($msec, $sec) = взорваться(" ", 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();
для ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["регион"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["адрес"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//Оценка завершается и выводятся результаты
эхо $dblTimeDuration эхо "rn";
// Преобразование кодировки запроса текстового файла оценки
$dblTimeStart = GetMicroTime();
для ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["регион"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["адрес"]);
}
$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 # БУКВА-МОДИФИКАТОР МАКРОН (первый тон китайского языка)
…
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 байта соответствуют коду ГБ, а последние 2 байта; соответствуют коду Unicode. Процедура преобразования следующая:
<?php
$arrLines = файл("gb_unicode.txt");
foreach ($arrLines как $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
ksort($arrCodeTable);
$intCount = count($arrCodeTable);
$strCount = chr($intCount % 256) .chr(этаж($intCount / 256));
$fileGBU = fopen("gbu.dat", "wb");
fwrite($fileGBU, $strCount);
foreach ($arrCodeTable as $k => $v) {
$strData = chr($k % 256) .chr(этаж($k/256)) .chr($v % 256) .chr(пол($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 = стрлен($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});
если ($intGB == $intCode) {
$intStart = $intMid;
перерыв;
}
если ($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});
если ($intGB == $intCode) {
$strBuf = fread($fileGBU, 2);
$intCodeU = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet .= u2utf8($intCodeU);
} еще {
$strRet .= "??";
}
$я++;
} еще {
$strRet .= $strGB{$i};
}
}
вернуть $стррет;
}
Добавьте его в исходную программу оценки и дважды одновременно оцените три метода, чтобы получить данные (с точностью до 3 знаков после запятой, единица измерения: секунды):
Метод MySQL: 0,125
Метод текстового файла: 10.873
Метод разделения двоичного файла пополам: 0,106
Метод MySQL: 0,102
Метод текстового файла: 10.677
Метод разделения двоичного файла пополам: 0,092.
Видно, что метод разделения двоичного файла пополам имеет небольшое преимущество перед методом MySQL. Однако все приведенные выше оценки перекодируют короткие географические местоположения. Что, если они перекодируют более длинные тексты? Я нашел 5 файлов Blog RSS 2.0, все в кодировке GB2312. Оцените время, необходимое для кодирования 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. Если текст, преобразуемый каждый раз, небольшой, можно использовать двоичный файл в сочетании с методом половинного преобразования, если текст, преобразуемый каждый раз, большой. удобно использовать текстовый файл для хранения таблицы перекодирования и один раз прочитать таблицу поиска в память перед преобразованием.