في المقالة "تقييم عنوان IP->تحويل الموقع الجغرافي"، ذكر أن استخدام وظيفة ip2addr لقراءة ملف قاعدة بيانات IP مباشرة هو الأكثر كفاءة مقارنة باستخدام قاعدة بيانات MySQL لتخزين بيانات IP، باستخدام استعلام SQL الأقل كفاءة. لكن ملف قاعدة بيانات IP QQWry.dat مشفر بـ GB2312. الآن أحتاج إلى نتائج تحديد الموقع الجغرافي المشفرة بـ UTF-8. إذا كنت تستخدم طريقة MySQL، فيمكنك تحويل البيانات إلى ترميز UTF-8 عندما يتم تخزينها في قاعدة البيانات، مرة واحدة وإلى الأبد. ومع ذلك، لا يمكن تعديل الملف QQWry.dat، ولا يمكن تحويل نتيجة إخراج الدالة ip2addr إلا ديناميكيًا.
هناك أربع طرق على الأقل لتحويل ترميز GB->UTF-8 ديناميكيًا:
استخدم امتداد PHP Iconv للتحويل،
استخدم امتداد mb_string الخاص بـ PHP للتحويل
، استخدم تحويل جدول المبادلة، ويتم تخزين جدول المبادلة
في
قاعدة بيانات MySQL باستخدام تحويل جدول المبادلة. ، جدول المبادلة لا يمكن استخدام أول طريقتينللتخزين في الملفات النصية
إلا إذا تم إعداد الخادم وفقًا لذلك (تم تجميع الامتدادات المقابلة وتثبيتها). لا يحتوي مضيفي الافتراضي على هذين الامتدادين، لذا يجب أن أفكر في الطريقتين الأخيرتين. لم يتم تقييم الطريقتين الأولين في هذه المقالة.
إجراء التقييم هو كما يلي (بالنسبة إلى func_ip.php، يرجى الرجوع إلى المقالة "تقييم عنوان IP->تحويل الموقع الجغرافي"):
<?php
require_once("func_ip.php");
الدالة u2utf8($c) {
$str = "";
إذا ($ج <0x80) {
$str .= $c;
} إلسيف ($ج <0x800) {
$str .= chr(0xC0 | $c >> 6);
$str .= chr(0x80 | $c & 0x3F);
} إلسيف ($ج <0x10000) {
$str .= chr(0xE0 | $c >> 12);
$str .= chr(0x80 | $c >> 6 & 0x3F);
$str .= chr(0x80 | $c & 0x3F);
} إلسيف ($ج <0x200000) {
$str .= chr(0xF0 | $c >> 18);
$str .= chr(0x80 | $c >> 12 & 0x3F);
$str .= chr(0x80 | $c >> 6 & 0x3F);
$str .= chr(0x80 | $c & 0x3F);
}
إرجاع $str;
}
الدالة GB2UTF8_SQL($strGB) {
إذا قام (!trim($strGB)) بإرجاع $strGB;
$strRet = "";
$intLen = strlen($strGB);
لـ ($i = 0; $i < $intLen; $i++) {
إذا (أورد($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"]);
else $strRet .= "؟؟";
$i++;
} آخر {
$strRet .= $strGB{$i};
}
}
إرجاع $strRet؛
}
الدالة GB2UTF8_FILE($strGB) {
إذا قام (!trim($strGB)) بإرجاع $strGB;
$arrLines = file("gb_unicode.txt");
foreach ($arrLines كـ $strLine) {
$arrCodeTable[hexdec(substr($strLine, 0, 6))] = hexdec(substr($strLine, 7, 6));
}
$strRet = "";
$intLen = strlen($strGB);
لـ ($i = 0; $i < $intLen; $i++) {
إذا (أورد($strGB{$i}) > 127) {
$strCurr = substr($strGB, $i, 2);
$intGB = hexdec(bin2hex($strCurr)) - 0x8080;
إذا ($arrCodeTable[$intGB]) $strRet .= u2utf8($arrCodeTable[$intGB]);
else $strRet .= "؟؟";
$i++;
} آخر {
$strRet .= $strGB{$i};
}
}
إرجاع $strRet؛
}
الدالة EncodeIp($strDotquadIp) {
$arrIpSep =تفجير('.', $strDotquadIp);
if (count($arrIpSep) != 4) return 0;
$intIp = 0;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
إرجاع $intIp;
//return sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
الدالة GetMicroTime() {
list($msec, $sec) = Explosion(" ", microtime());
إرجاع ((مزدوج)$msec + (مزدوج)$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]["region"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// ينتهي التقييم وتظهر النتائج
echo $dblTimeDuration; echo "rn";
// ترميز تحويل ملف نصي للتقييم query
$dblTimeStart = GetMicroTime();
لـ ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// ينتهي التقييم وتظهر النتائج
echo $dblTimeDuration; echo "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 # MODIFIER LETTER MACRON (النغمة الأولى باللغة الصينية الماندرين)
…
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 بايت تتوافق مع رمز GB، وآخر 2 بايت. تتوافق مع رمز Unicode. إجراء التحويل كما يلي:
<?php
$arrLines = file("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(floor($intCount / 256));
$fileGBU = fopen("gbu.dat", "wb");
fwrite($fileGBU, $strCount);
foreach ($arrCodeTable كـ $k => $v) {
$strData = chr($k % 256) .
fwrite($fileGBU, $strData);
}
fClose($fileGBU);
?>
بعد تنفيذ البرنامج، يتم الحصول على جدول المقارنة الثنائي GB->Unicode gbu.dat، ويتم فرز سجلات البيانات وفقًا لرمز GB، وهو مناسب للبحث بالطريقة النصفية. وظيفة تحويل الشفرة باستخدام gbu.dat هي كما يلي:
function GB2UTF8_FILE1($strGB) {
إذا قام (!trim($strGB)) بإرجاع $strGB;
$fileGBU = fopen("gbu.dat", "rb");
$strBuf = fread($fileGBU, 2);
$intCount = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet = "";
$intLen = strlen($strGB);
لـ ($i = 0; $i < $intLen; $i++) {
إذا (أورد($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;
else $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 .= "؟؟";
}
$i++;
} آخر {
$strRet .= $strGB{$i};
}
}
إرجاع $strRet؛
}
أضفه إلى برنامج التقييم الأصلي، وقم بتقييم الطرق الثلاث مرتين في نفس الوقت للحصول على البيانات (دقيقة حتى 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 إذا كان النص المحول في كل مرة صغيرًا، فمن المناسب استخدام ملف ثنائي مدمج مع طريقة التحويل النصفية إذا كان النص المحول في كل مرة كبيرًا. إنه مناسب لاستخدام ملف نصي لتخزين جدول البحث وقراءة جدول البحث في الذاكرة مرة واحدة قبل التحويل.