Dalam artikel "Evaluasi Alamat IP->Konversi Lokasi Geografis", disebutkan bahwa menggunakan fungsi ip2addr untuk langsung membaca file database IP adalah yang paling efisien. Dibandingkan dengan menggunakan database MySQL untuk menyimpan data IP, menggunakan query SQL adalah yang paling tidak efisien. Namun file database IP QQWry.dat dikodekan GB2312. Sekarang saya memerlukan hasil geolokasi yang dikodekan UTF-8. Jika Anda menggunakan metode MySQL, Anda dapat mengonversi data ke pengkodean UTF-8 saat disimpan dalam database, untuk selamanya. Namun, file QQWry.dat tidak dapat diubah, dan hasil keluaran dari fungsi ip2addr hanya dapat dikonversi secara dinamis.
Setidaknya ada empat cara untuk mengonversi pengkodean GB->UTF-8 secara dinamis:
gunakan ekstensi iconv PHP untuk mengonversi,
gunakan ekstensi mb_string PHP untuk mengonversi
, gunakan konversi tabel swap, dan tabel swap disimpan
di
database MySQL menggunakan konversi tabel swap , tabel swap Dua metodepenyimpanan pertama dalam file teks
hanya dapat digunakan jika server telah diatur sesuai (ekstensi yang sesuai telah dikompilasi dan diinstal). Host virtual saya tidak memiliki dua ekstensi ini, jadi saya harus mempertimbangkan dua metode terakhir. Dua metode pertama tidak dievaluasi dalam artikel ini.
Prosedur evaluasinya adalah sebagai berikut (untuk func_ip.php, silakan merujuk ke artikel "Evaluasi Alamat IP->Konversi Lokasi Geografis"):
<?php
require_once("func_ip.php");
fungsi u2utf8($c) {
$str = "";
jika ($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);
}
kembalikan $str;
}
fungsi GB2UTF8_SQL($strGB) {
jika (!trim($strGB)) mengembalikan $strGB;
$strRet = "";
$intLen = strlen($strGB);
untuk ($i = 0; $i < $intLen; $i++) {
jika (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
$strSql = "PILIH kode_unicode DARI nnstats_gb_unicode
DIMANA kode_gb = ".$intGB."BATAS 1"
;
$resResult = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
lain $strRet .= "??";
$i++;
} kalau tidak {
$strRet .= $strGB{$i};
}
}
kembalikan $strRet;
}
fungsi GB2UTF8_FILE($strGB) {
jika (!trim($strGB)) mengembalikan $strGB;
$arrLines = file("gb_unicode.txt");
foreach ($arrLines sebagai $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
$strRet = "";
$intLen = strlen($strGB);
untuk ($i = 0; $i < $intLen; $i++) {
jika (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
if ($arrCodeTable[$intGB]) $strRet .= u2utf8($arrCodeTable[$intGB]);
lain $strRet .= "??";
$i++;
} kalau tidak {
$strRet .= $strGB{$i};
}
}
kembalikan $strRet;
}
fungsi EncodeIp($strDotquadIp) {
$arrIpSep = meledak('.', $strDotquadIp);
jika (hitung($arrIpSep) != 4) kembalikan 0;
$intIp = 0;
foreach ($arrIpSep sebagai $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
kembalikan $intIp;
//kembalikan sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
fungsi DapatkanMicroTime() {
daftar($msec, $detik) = meledak(" ", microtime());
return ((ganda)$mdetik + (ganda)$detik);
}
for ($i = 0; $i < 100; $i++) { // Menghasilkan 100 alamat IP secara acak
$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("tes");
// Evaluasi konversi pengkodean query MySQL
$dblTimeStart = DapatkanMicroTime();
untuk ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["wilayah"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["alamat"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//Evaluasi berakhir dan hasilnya dikeluarkan
gema $dblWaktuDurasi; gema "rn";
// Pengkodean konversi kueri file teks evaluasi
$dblTimeStart = DapatkanMicroTime();
untuk ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["wilayah"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["alamat"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
//Evaluasi berakhir dan hasilnya dikeluarkan
gema $dblWaktuDurasi; gema "rn";
?>
Hasil dari dua evaluasi (akurat hingga 3 tempat desimal, satuannya adalah detik):
Konversi kueri MySQL: 0,112
Konversi kueri teks: 10,590
Konversi kueri MySQL: 0,099
Konversi query teks: 10.623
Terlihat bahwa saat ini metode MySQL jauh lebih unggul dibandingkan metode query file. Namun tidak perlu terburu-buru menggunakan metode MySQL sekarang, karena metode file teks sangat memakan waktu terutama karena harus membaca seluruh gb_unicode.txt ke dalam memori untuk setiap konversi, dan gb_unicode.txt adalah file teks dengan format berikut:
0x2121 0x3000 #IDEOGRAFISSPACE
0x2122 0x3001 #KOMA IDEOGRAFI
0x2123 0x3002 #BERHENTI PENUH IDEOGRAFI
0x2124 0x30FB #KATAKANA TITIK TENGAH
0x2125 0x02C9 # MODIFIER HURUF MACRON (nada pertama 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>
File teks tidak efisien, jadi pertimbangkan untuk mengonversi file teks menjadi file biner, lalu gunakan metode setengah jalan untuk menemukan file tanpa membaca seluruh file ke dalam memori. Format filenya adalah: header file 2 byte, menyimpan jumlah record; kemudian record disimpan dalam file satu per satu, setiap record berukuran 4 byte, 2 byte pertama sesuai dengan kode GB, dan 2 byte terakhir. sesuai dengan kode Unicode. Prosedur konversinya adalah sebagai berikut:
<?php
$arrLines = file("gb_unicode.txt");
foreach ($arrLines sebagai $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
ksort($arrCodeTable);
$intHitungan = hitungan($arrCodeTable);
$strCount = chr($intCount % 256) .
$fileGBU = fopen("gbu.dat", "wb");
fwrite($fileGBU, $strCount);
foreach ($arrCodeTable sebagai $k => $v) {
$strData = chr($k % 256) .chr(lantai($v / 256));
fwrite($fileGBU, $strData);
}
fclose($fileGBU);
?>
Setelah menjalankan program, tabel perbandingan biner GB->Unicode gbu.dat diperoleh, dan catatan data diurutkan menurut kode GB, yang memudahkan pencarian dengan metode setengah. Fungsi transcoding menggunakan gbu.dat adalah sebagai berikut:
function GB2UTF8_FILE1($strGB) {
jika (!trim($strGB)) mengembalikan $strGB;
$fileGBU = fopen("gbu.dat", "rb");
$strBuf = ketakutan($fileGBU, 2);
$intCount = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet = "";
$intLen = strlen($strGB);
untuk ($i = 0; $i < $intLen; $i++) {
jika (ord($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
$intMulai = 1;
$intEnd = $intHitungan;
while ($intStart < $intEnd - 1) { // Cari dengan metode setengah
$intMid = lantai(($intStart + $intEnd) / 2);
$intOffset = 2 + 4 * ($intMid - 1);
fseek($fileGBU, $intOffset);
$strBuf = ketakutan($fileGBU, 2);
$intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
if ($intGB == $intKode) {
$intStart = $intMid;
merusak;
}
if ($intGB > $intCode) $intStart = $intMid;
lain $intEnd = $intMid;
}
$intOffset = 2 + 4 * ($intStart - 1);
fseek($fileGBU, $intOffset);
$strBuf = ketakutan($fileGBU, 2);
$intCode = ord($strBuf{0}) + 256 * ord($strBuf{1});
if ($intGB == $intKode) {
$strBuf = ketakutan($fileGBU, 2);
$intCodeU = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet .= u2utf8($intCodeU);
} kalau tidak {
$strRet .= "??";
}
$i++;
} kalau tidak {
$strRet .= $strGB{$i};
}
}
kembalikan $strRet;
}
Tambahkan ke program evaluasi asli, dan evaluasi ketiga metode dua kali secara bersamaan untuk mendapatkan data (akurat hingga 3 tempat desimal, satuan: detik):
Metode MySQL: 0,125
Metode file teks: 10.873
Metode separuh file biner: 0,106
Metode MySQL: 0,102
Metode file teks: 10.677
Metode separuh file biner: 0,092
Terlihat bahwa metode separuh file biner memiliki sedikit keunggulan dibandingkan metode MySQL. Namun, semua evaluasi di atas mentranskode lokasi geografis yang pendek. Bagaimana jika mereka mentranskode teks yang lebih panjang? Saya menemukan 5 file Blog RSS 2.0, semuanya dikodekan dalam GB2312. Evaluasi waktu yang diperlukan untuk menyandikan 5 file menggunakan ketiga metode tersebut. Kedua data pengukuran tersebut adalah sebagai berikut (akurat hingga 3 tempat desimal, satuan: detik):
Metode MySQL: 7.206
Metode file teks: 0,772
Metode separuh file biner: 5.022
Metode MySQL: 7.440
Metode file teks: 0.766
Metode separuh file biner: 5.055
Terlihat bahwa metode file teks optimal untuk teks yang panjang, karena setelah tabel perbandingan transcoding dibaca ke dalam memori, transcoding bisa menjadi sangat efisien. Dalam hal ini, kita juga dapat mencoba memperbaikinya dan mengubah metode file teks menjadi: tabel perbandingan transcoding dibaca ke dalam memori dari file biner gbu.dat, bukan dari file teks. Data evaluasinya sebagai berikut (akurasi dan satuannya sama seperti di atas):
Baca tabel perbandingan dari file teks: 0,766
Membaca tabel pencarian dari file biner: 0,831
Membaca tabel pencarian dari file teks: 0,774
Membaca tabel perbandingan dari file biner: 0,833
menunjukkan bahwa perbaikan ini gagal, dan membaca tabel perbandingan transcoding dari file teks lebih efisien.
Ringkasan: Gunakan PHP untuk secara dinamis mengonversi pengkodean GB ke pengkodean UTF-8. Jika teks yang dikonversi setiap kali berukuran kecil, sebaiknya menggunakan file biner yang dikombinasikan dengan metode konversi setengah jalan; sangat cocok untuk menggunakan file teks untuk menyimpan transcoding. Tabel pencarian, dan membaca tabel pencarian ke dalam memori satu kali sebelum konversi.