En el artículo "Evaluación de dirección IP-> Conversión de ubicación geográfica", se menciona que usar la función ip2addr para leer directamente el archivo de la base de datos IP es el más eficiente. En comparación con el uso de la base de datos MySQL para almacenar datos IP, usar consultas SQL es lo más eficiente. el menos eficiente. Pero el archivo de base de datos IP QQWry.dat está codificado en GB2312. Ahora necesito resultados de geolocalización codificados en UTF-8. Si utiliza el método MySQL, puede convertir los datos a codificación UTF-8 cuando se almacenan en la base de datos, de una vez por todas. Sin embargo, el archivo QQWry.dat no se puede modificar y el resultado de salida de la función ip2addr solo se puede convertir dinámicamente.
Hay al menos cuatro formas de convertir dinámicamente la codificación GB->UTF-8:
use la extensión iconv de PHP para convertir,
use la extensión mb_string de PHP para convertir
, use la conversión de tabla de intercambio y la tabla de intercambio se almacene
en
la base de datos MySQL usando la conversión de tabla de intercambio. , tabla de intercambio Los dos primeros métodosde almacenamiento en archivos de texto
sólo se pueden utilizar si el servidor se ha configurado correspondientemente (se han compilado e instalado las extensiones correspondientes). Mi host virtual no tiene estas dos extensiones, así que debo considerar los dos últimos métodos. Los dos primeros métodos no se evalúan en este artículo.
El procedimiento de evaluación es el siguiente (para func_ip.php, consulte el artículo "Evaluación de dirección IP->Conversión de ubicación geográfica"):
<?php
require_once ("func_ip.php");
función u2utf8($c) {
$cadena = "";
si ($c < 0x80) {
$cadena .= $c;
} elseif ($c < 0x800) {
$cadena .= chr(0xC0 | $c >> 6);
$cadena .= chr(0x80 | $c & 0x3F);
} elseif ($c < 0x10000) {
$cadena .= chr(0xE0 | $c >> 12);
$cadena .= chr(0x80 | $c >> 6 & 0x3F);
$cadena .= chr(0x80 | $c & 0x3F);
} elseif ($c < 0x200000) {
$cadena .= chr(0xF0 | $c >> 18);
$cadena .= chr(0x80 | $c >> 12 & 0x3F);
$cadena .= chr(0x80 | $c >> 6 & 0x3F);
$cadena .= chr(0x80 | $c & 0x3F);
}
devolver $cadena;
}
función GB2UTF8_SQL($strGB) {
si (!trim($strGB)) devuelve $strGB;
$strRet = "";
$intLen = strlen($strGB);
para ($i = 0; $i < $intLen; $i++) {
si (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
$strSql = "SELECCIONAR code_unicode DE nnstats_gb_unicode
DONDE code_gb = ".$intGB." LÍMITE 1"
;
$resResultado = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
else $strRet .= "??";
$yo++;
} demás {
$strRet .= $strGB{$i};
}
}
devolver $cadenaRet;
}
función GB2UTF8_FILE($strGB) {
si (!trim($strGB)) devuelve $strGB;
$arrLines = archivo("gb_unicode.txt");
foreach ($arrLines como $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
$strRet = "";
$intLen = strlen($strGB);
para ($i = 0; $i < $intLen; $i++) {
si (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
if ($arrCodeTable[$intGB]) $strRet .= u2utf8($arrCodeTable[$intGB]);
else $strRet .= "??";
$yo++;
} demás {
$strRet .= $strGB{$i};
}
}
devolver $cadenaRet;
}
función CodificarIp($strDotquadIp) {
$arrIpSep = explotar('.', $strDotquadIp);
si (count($arrIpSep) != 4) devuelve 0;
$intIP = 0;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
devolver $intIp;
//return sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
función ObtenerMicroTime() {
lista($msec, $sec) = explotar(" ", microtime());
return ((doble)$mseg + (doble)$seg);
}
for ($i = 0; $i < 100; $i++) { // Genera aleatoriamente 100 direcciones 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("prueba");
// Evaluar la conversión de codificación de consultas MySQL
$dblTimeStart = GetMicroTime();
para ($i = 0; $i < 100; $i++) {
$strUTF8Región = GB2UTF8_SQL($arrAddr[$i]["región"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["dirección"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//La evaluación finaliza y se emiten los resultados.
echo $dblTimeDuration; echo "rn";
// Conversión de codificación de la consulta del archivo de texto de evaluación
$dblTimeStart = GetMicroTime();
para ($i = 0; $i < 100; $i++) {
$strUTF8Región = GB2UTF8_FILE($arrAddr[$i]["región"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["dirección"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//La evaluación finaliza y se emiten los resultados.
echo $dblTimeDuration; echo "rn";
?>
Resultados de dos evaluaciones (con una precisión de 3 decimales, la unidad es el segundo):
Conversión de consultas MySQL: 0,112
Conversión de consultas de texto: 10.590
Conversión de consultas MySQL: 0.099
Conversión de consulta de texto: 10.623
Se puede ver que esta vez el método MySQL está muy por delante del método de consulta de archivos. Pero no hay prisa por usar el método MySQL ahora, porque el método del archivo de texto requiere mucho tiempo, principalmente porque tiene que leer el gb_unicode.txt completo en la memoria para cada conversión, y gb_unicode.txt es un archivo de texto con el siguiente formato:
0x2121 0x3000 #IDEOGRAPHICSPACE
0x2122 0x3001 #COMA IDEOGRÁFICA
0x2123 0x3002 #IDEOGRAFICO PUNTO COMPLETO
0x2124 0x30FB # PUNTO MEDIO KATAKANA
0x2125 0x02C9 # MACRON DE LETRA MODIFICADORA (primer tono del chino mandarín)
…
0x552A 0x6458 # <CJK>
0x552B 0x658B # <CJK>
0x552C 0x5B85 # <CJK>
0x552D 0x7A84 # <CJK>
…
0x777B 0x9F37 # <CJK>
0x777C 0x9F3D # <CJK>
0x777D 0x9F3E # <CJK>
0x777E 0x9F44 # <CJK>
El archivo de texto es ineficiente, así que considere convertir el archivo de texto en un archivo binario y luego use el método intermedio para buscar el archivo sin leer todo el archivo en la memoria. El formato del archivo es: el encabezado del archivo es de 2 bytes y almacena el número de registros, luego los registros se almacenan en el archivo uno por uno, cada registro tiene 4 bytes, los primeros 2 bytes corresponden al código GB y los últimos 2 bytes. corresponden al código Unicode. El procedimiento de conversión es el siguiente:
<?php
$arrLines = archivo("gb_unicode.txt");
foreach ($arrLines como $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
ksort($arrCodeTable);
$intCount = contar($arrCodeTable);
$strCount = chr($intCount % 256) .chr(piso($intCount / 256));
$archivoGBU = fopen("gbu.dat", "wb");
fwrite($archivoGBU, $strCount);
foreach ($arrCodeTable como $k => $v) {
$strData = chr($k % 256) .chr(piso($k / 256)) chr($v % 256) .
fwrite($archivoGBU, $strData);
}
fclose($archivoGBU);
?>
Después de ejecutar el programa, se obtiene la tabla de comparación binaria GB->Unicode gbu.dat y los registros de datos se clasifican según el código GB, lo cual es conveniente para buscar mediante el método medio. La función para transcodificar usando gbu.dat es la siguiente:
function GB2UTF8_FILE1($strGB) {
si (!trim($strGB)) devuelve $strGB;
$archivoGBU = fopen("gbu.dat", "rb");
$strBuf = fread($archivoGBU, 2);
$intCount = orden($strBuf{0}) + 256 * orden($strBuf{1});
$strRet = "";
$intLen = strlen($strGB);
para ($i = 0; $i < $intLen; $i++) {
si (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
$intInicio = 1;
$intEnd = $intCount;
while ($intStart < $intEnd - 1) { // Método de búsqueda por la mitad
$intMid = piso(($intStart + $intEnd) / 2);
$intOffset = 2 + 4 * ($intMid - 1);
fseek($archivoGBU, $intOffset);
$strBuf = fread($archivoGBU, 2);
$intCode = orden($strBuf{0}) + 256 * orden($strBuf{1});
si ($intGB == $intCode) {
$intInicio = $intMid;
romper;
}
if ($intGB > $intCode) $intStart = $intMid;
else $intEnd = $intMid;
}
$intOffset = 2 + 4 * ($intInicio - 1);
fseek($archivoGBU, $intOffset);
$strBuf = fread($archivoGBU, 2);
$intCode = orden($strBuf{0}) + 256 * orden($strBuf{1});
si ($intGB == $intCode) {
$strBuf = fread($archivoGBU, 2);
$intCodeU = orden($strBuf{0}) + 256 * orden($strBuf{1});
$strRet .= u2utf8($intCodeU);
} demás {
$strRet .= "??";
}
$yo++;
} demás {
$strRet .= $strGB{$i};
}
}
devolver $cadenaRet;
}
Agréguelo al programa de evaluación original y evalúe los tres métodos dos veces al mismo tiempo para obtener los datos (con una precisión de 3 decimales, unidad: segundos):
Método MySQL: 0,125
Método de archivo de texto: 10.873
Método de reducción a la mitad de archivos binarios: 0.106
Método MySQL: 0.102
Método de archivo de texto: 10.677
Método de reducción a la mitad de archivos binarios: 0.092
Se puede ver que el método de reducción a la mitad de archivos binarios tiene una ligera ventaja sobre el método MySQL. Sin embargo, todas las evaluaciones anteriores transcodifican ubicaciones geográficas cortas. ¿Qué pasa si transcodifican textos más largos? Encontré 5 archivos Blog RSS 2.0, todos codificados en GB2312. Evalúe el tiempo que lleva codificar 5 archivos utilizando los tres métodos. Los dos datos de medición son los siguientes (con una precisión de 3 decimales, unidad: segundos):
Método MySQL: 7.206.
Método de archivo de texto: 0,772
Método de reducción a la mitad de archivos binarios: 5.022
Método MySQL: 7.440
Método de archivo de texto: 0,766
Método de reducción a la mitad de archivos binarios: 5.055
Se puede ver que el método de archivos de texto es óptimo para textos largos, porque después de leer la tabla de comparación de transcodificación en la memoria, la transcodificación puede ser muy eficiente. En este caso, también podemos intentar mejorarlo y cambiar el método del archivo de texto a: la tabla de comparación de transcodificación se lee en la memoria desde el archivo binario gbu.dat en lugar del archivo de texto. Los datos de evaluación son los siguientes (la precisión y la unidad son las mismas que las anteriores):
Lea la tabla de comparación del archivo de texto: 0,766
Lectura de tabla de búsqueda desde archivo binario: 0.831
Lectura de tabla de búsqueda desde archivo de texto: 0.774
Leer la tabla de comparación del archivo binario: 0.833
indica que esta mejora ha fallado y leer la tabla de comparación de transcodificación del archivo de texto es más eficiente.
Resumen: use PHP para convertir dinámicamente la codificación GB a codificación UTF-8. Si el texto convertido cada vez es pequeño, es adecuado usar un archivo binario combinado con el método de conversión intermedia si el texto convertido cada vez es grande. es adecuado utilizar un archivo de texto para almacenar la tabla de búsqueda y leer la tabla de búsqueda en la memoria una vez antes de la conversión.