Dans l'article « Évaluation de l'adresse IP->Conversion de l'emplacement géographique », il est mentionné que l'utilisation de la fonction ip2addr pour lire directement le fichier de la base de données IP est la plus efficace. Par rapport à l'utilisation de la base de données MySQL pour stocker les données IP, l'utilisation d'une requête SQL est plus efficace. le moins efficace. Mais le fichier de base de données IP QQWry.dat est codé en GB2312. Maintenant, j'ai besoin de résultats de géolocalisation codés en UTF-8. Si vous utilisez la méthode MySQL, vous pouvez convertir les données en codage UTF-8 lorsqu'elles sont stockées dans la base de données, une fois pour toutes. Cependant, le fichier QQWry.dat ne peut pas être modifié et le résultat de sortie de la fonction ip2addr ne peut être converti que dynamiquement.
Il existe au moins quatre façons de convertir dynamiquement l'encodage GB->UTF-8 :
utilisez l'extension iconv de PHP pour convertir,
utilisez l'extension mb_string de PHP pour convertir
, utilisez la conversion de table d'échange, et la table d'échange est stockée
dans
la base de données MySQL à l'aide de la conversion de table d'échange. , table d'échange Les deux premières méthodesde stockage dans des fichiers texte
ne peuvent être utilisées que si le serveur a été configuré en conséquence (les extensions correspondantes ont été compilées et installées). Mon hôte virtuel ne dispose pas de ces deux extensions, je dois donc considérer les deux dernières méthodes. Les deux premières méthodes ne sont pas évaluées dans cet article.
La procédure d'évaluation est la suivante (pour func_ip.php, veuillez vous référer à l'article "Évaluation de l'adresse IP->Conversion de localisation géographique") :
<?php
require_once ("func_ip.php");
fonction u2utf8($c) {
$str = "";
si ($c < 0x80) {
$str .= $c;
} elseif ($c < 0x800) {
$str .= chr(0xC0 | $c >> 6);
$str .= chr(0x80 | $c & 0x3F);
} autre si ($c < 0x10000) {
$str .= chr(0xE0 | $c >> 12);
$str .= chr(0x80 | $c >> 6 & 0x3F);
$str .= chr(0x80 | $c & 0x3F);
} autre si ($c < 0x200000) {
$str .= chr(0xF0 | $c >> 18);
$str .= chr(0x80 | $c >> 12 & 0x3F);
$str .= chr(0x80 | $c >> 6 & 0x3F);
$str .= chr(0x80 | $c & 0x3F);
}
retourner $ str ;
}
fonction GB2UTF8_SQL($strGB) {
if (!trim($strGB)) renvoie $strGB ;
$strRet = "";
$intLen = strlen($strGB);
pour ($i = 0; $i < $intLen; $i++) {
si (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
$strSql = "SELECT code_unicode FROM nnstats_gb_unicode
OÙ code_gb = ".$intGB."
;
$resResult = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
sinon $strRet .= "??";
$i++;
} autre {
$strRet .= $strGB{$i};
}
}
retourner $strRet ;
}
fonction GB2UTF8_FILE($strGB) {
if (!trim($strGB)) renvoie $strGB ;
$arrLines = fichier("gb_unicode.txt");
foreach ($arrLines comme $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
$strRet = "";
$intLen = strlen($strGB);
pour ($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]);
sinon $strRet .= "??";
$i++;
} autre {
$strRet .= $strGB{$i};
}
}
retourner $strRet ;
}
fonction EncodeIp($strDotquadIp) {
$arrIpSep = exploser('.', $strDotquadIp);
if (count($arrIpSep) != 4) renvoie 0 ;
$intIp = 0 ;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
retourner $intIp ;
//retour sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
fonction GetMicroTime() {
list($msec, $sec) = éclater(" ", microtime());
return ((double)$msec + (double)$sec);
}
for ($i = 0; $i < 100; $i++) { // Génère aléatoirement 100 adresses 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");
// Évaluer la conversion d'encodage des requêtes MySQL
$dblTimeStart = GetMicroTime();
pour ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["adresse"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart ;
// L'évaluation se termine et les résultats sont affichés
echo $dblTimeDuration; echo "rn";
// Conversion d'encodage de la requête du fichier texte d'évaluation
$dblTimeStart = GetMicroTime();
pour ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["adresse"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart ;
// L'évaluation se termine et les résultats sont affichés
echo $dblTimeDuration; echo "rn";
?>
Résultats de deux évaluations (précis à 3 décimales près, l'unité est la seconde) :
Conversion de requête MySQL : 0,112
Conversion de requête texte : 10,590
Conversion de requête MySQL : 0,099
Conversion de requête texte : 10,623
On constate que cette fois la méthode MySQL est bien en avance sur la méthode de requête de fichier. Mais il n'y a pas d'urgence à utiliser la méthode MySQL maintenant, car la méthode du fichier texte prend beaucoup de temps, principalement parce qu'elle doit lire l'intégralité du gb_unicode.txt dans la mémoire pour chaque conversion, et gb_unicode.txt est un fichier texte avec le format suivant :
0x2121 0x3000 #IDEOGRAPHICSPACE
0x2122 0x3001 #VIRGULE IDÉOGRAPHIQUE
0x2123 0x3002 #POINT IDÉOGRAPHIQUE
0x2124 0x30FB # POINT MOYEN KATAKANA
0x2125 0x02C9 # LETTRE MODIFICATIVE MACRON (premier ton du chinois mandarin)
…
0x552A 0x6458 # <CJK>
0x552B 0x658B # <CJK>
0x552C 0x5B85 # <CJK>
0x552D 0x7A84 # <CJK>
…
0x777B 0x9F37 # <CJK>
0x777C 0x9F3D # <CJK>
0x777D 0x9F3E # <CJK>
0x777E 0x9F44 # <CJK>
Le fichier texte est inefficace, pensez donc à convertir le fichier texte en fichier binaire, puis utilisez la méthode intermédiaire pour trouver le fichier sans lire l'intégralité du fichier en mémoire. Le format de fichier est le suivant : l'en-tête du fichier fait 2 octets, stockant le nombre d'enregistrements ; puis les enregistrements sont stockés dans le fichier un par un, chaque enregistrement fait 4 octets, les 2 premiers octets correspondent au code GB et les 2 derniers octets. correspondent au code Unicode. La procédure de conversion est la suivante :
<?php
$arrLines = fichier("gb_unicode.txt");
foreach ($arrLines comme $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
ksort($arrCodeTable);
$intCount = nombre($arrCodeTable);
$strCount = chr($intCount % 256) . chr(floor($intCount / 256));
$fileGBU = fopen("gbu.dat", "wb");
fwrite($fileGBU, $strCount);
foreach ($arrCodeTable comme $k => $v) {
$strData = chr($k % 256) . chr(étage($k / 256)) chr($v % 256) .
fwrite($fileGBU, $strData);
}
fclose($fichierGBU);
?>
Après l'exécution du programme, le tableau de comparaison binaire GB->Unicode gbu.dat est obtenu et les enregistrements de données sont triés selon le code GB, ce qui est pratique pour la recherche par la méthode semi-automatique. La fonction de transcodage à l'aide de gbu.dat est la suivante :
function GB2UTF8_FILE1($strGB) {
if (!trim($strGB)) renvoie $strGB ;
$fileGBU = fopen("gbu.dat", "rb");
$strBuf = fread($fileGBU, 2);
$intCount = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet = "";
$intLen = strlen($strGB);
pour ($i = 0; $i < $intLen; $i++) {
si (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
$intDébut = 1 ;
$intEnd = $intCount ;
while ($intStart < $intEnd - 1) { // Méthode de recherche par moitié
$intMid = étage(($intStart + $intEnd) / 2);
$intOffset = 2 + 4 * ($intMid - 1);
fseek($fileGBU, $intOffset);
$strBuf = fread($fileGBU, 2);
$intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
si ($intGB == $intCode) {
$intDébut = $intMid ;
casser;
}
si ($intGB > $intCode) $intStart = $intMid;
sinon $intEnd = $intMid;
}
$intOffset = 2 + 4 * ($intStart - 1);
fseek($fileGBU, $intOffset);
$strBuf = fread($fileGBU, 2);
$intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
si ($intGB == $intCode) {
$strBuf = fread($fileGBU, 2);
$intCodeU = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet .= u2utf8($intCodeU);
} autre {
$strRet .= "??";
}
$i++;
} autre {
$strRet .= $strGB{$i};
}
}
retourner $strRet ;
}
Ajoutez-le au programme d'évaluation d'origine et évaluez les trois méthodes deux fois en même temps pour obtenir les données (précises à 3 décimales, unité : secondes) :
méthode MySQL : 0,125
Méthode fichier texte : 10.873
Méthode de réduction de moitié des fichiers binaires : 0,106
Méthode MySQL : 0,102
Méthode fichier texte : 10,677
Méthode de réduction de moitié des fichiers binaires : 0,092
On peut voir que la méthode de réduction de moitié des fichiers binaires a un léger avantage par rapport à la méthode MySQL. Cependant, les évaluations ci-dessus transcodent toutes des emplacements géographiques courts. Et si elles transcodent des textes plus longs ? J'ai trouvé 5 fichiers Blog RSS 2.0, tous encodés en GB2312. Évaluez le temps nécessaire pour encoder 5 fichiers en utilisant les trois méthodes. Les deux données de mesure sont les suivantes (précision à 3 décimales, unité : secondes) :
Méthode MySQL : 7,206.
Méthode fichier texte : 0,772
Méthode de réduction de moitié des fichiers binaires : 5,022
Méthode MySQL : 7,440
Méthode fichier texte : 0,766
Méthode de réduction de moitié du fichier binaire : 5,055
On peut voir que la méthode du fichier texte est optimale pour les textes longs, car une fois le tableau de comparaison de transcodage lu dans la mémoire, le transcodage peut être très efficace. Dans ce cas, nous pouvons également essayer de l'améliorer et changer la méthode du fichier texte comme suit : la table de comparaison de transcodage est lue dans la mémoire à partir du fichier binaire gbu.dat au lieu du fichier texte. Les données d'évaluation sont les suivantes (la précision et l'unité sont les mêmes que ci-dessus) :
Lisez le tableau de comparaison à partir du fichier texte : 0,766
Lecture de la table de recherche à partir d'un fichier binaire : 0,831
Lecture de la table de recherche à partir d'un fichier texte : 0,774
Lecture du tableau de comparaison à partir du fichier binaire : 0,833
indique que cette amélioration a échoué, et la lecture du tableau de comparaison de transcodage à partir du fichier texte est plus efficace.
Résumé : utilisez PHP pour convertir dynamiquement l'encodage GB en encodage UTF-8. Si le texte converti à chaque fois est petit, il convient d'utiliser un fichier binaire combiné avec la méthode de conversion à mi-chemin si le texte converti à chaque fois est volumineux. il convient d'utiliser un fichier texte pour stocker la table de recherche et de lire la table de recherche en mémoire une fois avant la conversion.