No artigo "Avaliação de endereço IP-> Conversão de localização geográfica", é mencionado que usar a função ip2addr para ler diretamente o arquivo de banco de dados IP é o mais eficiente. Em comparação com o uso do banco de dados MySQL para armazenar dados IP, usar consulta SQL é. o menos eficiente. Mas o arquivo de banco de dados IP QQWry.dat é codificado em GB2312. Agora preciso de resultados de geolocalização codificados em UTF-8. Se você usar o método MySQL, poderá converter os dados para a codificação UTF-8 quando armazenados no banco de dados, de uma vez por todas. No entanto, o arquivo QQWry.dat não pode ser modificado e o resultado de saída da função ip2addr só pode ser convertido dinamicamente.
Existem pelo menos quatro maneiras de converter dinamicamente a codificação GB-> UTF-8:
usar a extensão iconv do PHP para converter,
usar a extensão mb_string do PHP para converter
, usar conversão de tabela de troca e a tabela de troca é armazenada
no
banco de dados MySQL usando conversão de tabela de troca , tabela de troca Os dois primeiros métodosde armazenamento em arquivos de texto
só podem ser usados se o servidor tiver sido configurado adequadamente (as extensões correspondentes foram compiladas e instaladas). Meu host virtual não possui essas duas extensões, então tenho que considerar os dois últimos métodos. Os dois primeiros métodos não são avaliados neste artigo.
O procedimento de avaliação é o seguinte (para func_ip.php, consulte o artigo "Avaliação de endereço IP->Conversão de localização geográfica"):
<?php
require_once("func_ip.php");
função u2utf8($c){
$str = "";
se ($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);
}
retornar $str;
}
função GB2UTF8_SQL($strGB) {
if (!trim($strGB)) retornar $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 = "SELECIONE code_unicode DE nnstats_gb_unicode
ONDE code_gb = ".$intGB."
;
$resResultado = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
senão $strRet .= "??";
$eu++;
} outro {
$strRet .= $strGB{$i};
}
}
retornar $strRet;
}
função GB2UTF8_FILE($strGB) {
if (!trim($strGB)) retornar $strGB;
$arrLines = arquivo("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]);
senão $strRet .= "??";
$eu++;
} outro {
$strRet .= $strGB{$i};
}
}
retornar $strRet;
}
função EncodeIp($strDotquadIp) {
$arrIpSep = explode('.', $strDotquadIp);
if (contagem($arrIpSep) != 4) retornar 0;
$intIp = 0;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
return $intIp;
//retorna sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
função GetMicroTime() {
list($msec, $sec) = explode(" ", microtime());
return ((duplo)$msec + (duplo)$sec);
}
for ($i = 0; $i < 100; $i++) { // Gera aleatoriamente 100 endereços 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("teste");
// Avalia a conversão de codificação de consultas MySQL
$dblTimeStart = GetMicroTime();
para ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["região"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["endereço"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//A avaliação termina e os resultados são exibidos
echo $dblTimeDuration;
//Conversão de codificação da consulta do arquivo de texto de avaliação
$dblTimeStart = GetMicroTime();
para ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["região"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["endereço"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//A avaliação termina e os resultados são exibidos
echo $dblTimeDuration;
?>
Resultados de duas avaliações (com precisão de 3 casas decimais, a unidade é o segundo):
Conversão de consulta MySQL: 0,112
Conversão de consulta de texto: 10,590
Conversão de consulta MySQL: 0,099
Conversão de consulta de texto: 10.623
Pode-se observar que desta vez o método MySQL está muito à frente do método de consulta de arquivo. Mas não há pressa em usar o método MySQL agora, porque o método de arquivo de texto consome muito tempo, principalmente porque precisa ler todo o gb_unicode.txt na memória para cada conversão, e gb_unicode.txt é um arquivo de texto com o seguinte formato:
0x2121 0x3000 #IDEOGRAPHICSPACE
0x2122 0x3001 #Vírgula IDEOGRÁFICA
0x2123 0x3002 #IDEOGRÁFICO PONTO COMPLETO
0x2124 0x30FB # KATAKANA PONTO MÉDIO
0x2125 0x02C9 # MODIFICADOR LETRA MACRON (primeiro tom do mandarim)
…
0x552A 0x6458#<CJK>
0x552B 0x658B#<CJK>
0x552C 0x5B85#<CJK>
0x552D 0x7A84#<CJK>
…
0x777B 0x9F37#<CJK>
0x777C 0x9F3D#<CJK>
0x777D 0x9F3E#<CJK>
0x777E 0x9F44 # <CJK>
O arquivo de texto é ineficiente, então considere converter o arquivo de texto em um arquivo binário e, em seguida, use o método intermediário para localizar o arquivo sem ler o arquivo inteiro na memória. O formato do arquivo é: o cabeçalho do arquivo tem 2 bytes, armazenando o número de registros, então os registros são armazenados no arquivo um por um, cada registro tem 4 bytes, os primeiros 2 bytes correspondem ao código GB e os últimos 2 bytes; correspondem ao código Unicode. O procedimento de conversão é o seguinte:
<?php
$arrLines = arquivo("gb_unicode.txt");
foreach ($arrLines as $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
ksort($arrCodeTable);
$intCount = contagem($arrCodeTable);
$strCount = chr($intCount % 256) chr(floor($intCount / 256));
$fileGBU = fopen("gbu.dat", "wb");
fwrite($arquivoGBU, $strCount);
foreach ($arrCodeTable as $k => $v) {
$strData = chr($k % 256) chr(piso($k / 256)) chr($v % 256) .
fwrite($arquivoGBU, $strData);
}
fclose($arquivoGBU);
?>
Após a execução do programa, a tabela de comparação binária GB->Unicode gbu.dat é obtida e os registros de dados são classificados de acordo com o código GB, o que é conveniente para pesquisa pelo método half. A função para transcodificação usando gbu.dat é a seguinte:
function GB2UTF8_FILE1($strGB) {
if (!trim($strGB)) retornar $strGB;
$fileGBU = fopen("gbu.dat", "rb");
$strBuf = fread($arquivoGBU, 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;
$intIniciar = 1;
$intEnd = $intCount;
while ($intStart < $intEnd - 1) { // Pesquisa por meio método
$intMid = piso(($intStart + $intEnd) / 2);
$intOffset = 2 + 4 * ($intMid - 1);
fseek($fileGBU, $intOffset);
$strBuf = fread($arquivoGBU, 2);
$intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
if ($intGB == $intCode) {
$intStart = $intMid;
quebrar;
}
if ($intGB > $intCode) $intStart = $intMid;
senão $intEnd = $intMid;
}
$intOffset = 2 + 4 * ($intStart - 1);
fseek($fileGBU, $intOffset);
$strBuf = fread($arquivoGBU, 2);
$intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
if ($intGB == $intCode) {
$strBuf = fread($arquivoGBU, 2);
$intCodeU = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet.=u2utf8($intCodeU);
} outro {
$strRet.= "??";
}
$eu++;
} outro {
$strRet .= $strGB{$i};
}
}
retornar $strRet;
}
Adicione-o ao programa de avaliação original e avalie os três métodos duas vezes ao mesmo tempo para obter os dados (com precisão de 3 casas decimais, unidade: segundos):
Método MySQL: 0,125
Método de arquivo de texto: 10.873
Método de redução pela metade do arquivo binário: 0,106
Método MySQL: 0,102
Método de arquivo de texto: 10.677
Método de redução pela metade do arquivo binário: 0,092
Pode-se observar que o método de redução pela metade do arquivo binário tem uma ligeira vantagem sobre o método MySQL. No entanto, todas as avaliações acima transcodificam localizações geográficas curtas. E se transcodificarem textos mais longos? Encontrei 5 arquivos RSS 2.0 do Blog, todos codificados em GB2312. Avalie o tempo necessário para codificar 5 arquivos usando os três métodos. Os dois dados de medição são os seguintes (com precisão de 3 casas decimais, unidade: segundos):
Método MySQL: 7.206.
Método de arquivo de texto: 0,772
Método de redução pela metade do arquivo binário: 5.022
Método MySQL: 7.440
Método de arquivo de texto: 0,766
Método de redução pela metade do arquivo binário: 5.055
Pode-se observar que o método do arquivo de texto é ideal para textos longos, pois após a leitura da tabela de comparação de transcodificação na memória, a transcodificação pode ser muito eficiente. Neste caso, também podemos tentar melhorá-lo e alterar o método do arquivo de texto para: a tabela de comparação de transcodificação é lida na memória a partir do arquivo binário gbu.dat em vez do arquivo de texto. Os dados de avaliação são os seguintes (a precisão e a unidade são iguais às acima):
Leia a tabela de comparação do arquivo de texto: 0,766
Lendo a tabela de pesquisa do arquivo binário: 0,831
Lendo a tabela de pesquisa do arquivo de texto: 0,774
Lendo a tabela de comparação do arquivo binário: 0,833
indica que essa melhoria falhou e a leitura da tabela de comparação de transcodificação do arquivo de texto é mais eficiente.
Resumo: Use PHP para converter dinamicamente a codificação GB para codificação UTF-8. Se o texto convertido for pequeno, é adequado usar um arquivo binário combinado com o método de conversão intermediária se o texto convertido for grande; é adequado usar um arquivo de texto para armazenar a tabela de consulta e ler a tabela de consulta na memória uma vez antes da conversão.