正则表达式用于字符串处理、表单验证等场合,实用高效。
匹配中文字符的正则表达式: [u4e00-u9fa5]
评注:匹配中文还真是个头疼的事,有了这个表达式就好办了
匹配双字节字符(包括汉字在内):[^x00-xff]
评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
匹配空白行的正则表达式: s*
评注:可以用来删除空白行
匹配HTML标记的正则表达式:<(S*?)[^>]*>.*?</1>|<.*? />
评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力
匹配首尾空白字符的正则表达式:^s*|s*$
评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式
匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
评注:表单验证时很实用
匹配网址URL的正则表达式:[a-zA-z]+://[^s]*
评注:网上流传的版本功能很有限,上面这个基本可以满足需求
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
评注:表单验证时很实用
匹配国内电话号码:d{3}-d{8}|d{4}-d{7}
评注:匹配形式如 0511-4405222 或 021-87888822
匹配腾讯QQ号:[1-9][0-9]{4,}
评注:腾讯QQ号从10000开始
匹配中国邮政编码:[1-9]d{5}(?!d)
评注:中国邮政编码为6位数字
匹配身份证:d{15}|d{18}
评注:中国的身份证为15位或18位
匹配ip地址:d+.d+.d+.d+
评注:提取ip地址时有用
匹配特定数字:
^[1-9]d*$ //匹配正整数
^-[1-9]d*$ //匹配负整数
^-?[1-9]d*$ //匹配整数
^[1-9]d*|0$ //匹配非负整数(正整数 + 0)
^-[1-9]d*|0$ //匹配非正整数(负整数 + 0)
^[1-9]d*.d*|0.d*[1-9]d*$ //匹配正浮点数
^-([1-9]d*.d*|0.d*[1-9]d*)$ //匹配负浮点数
^-?([1-9]d*.d*|0.d*[1-9]d*|0?.0+|0)$ //匹配浮点数
^[1-9]d*.d*|0.d*[1-9]d*|0?.0+|0$ //匹配非负浮点数(正浮点数 + 0)
^(-([1-9]d*.d*|0.d*[1-9]d*))|0?.0+|0$ //匹配非正浮点数(负浮点数 + 0)
评注:处理大量数据时有用,具体应用时注意修正
匹配特定字符串:
^[A-Za-z]+$ //匹配由26个英文字母组成的字符串
^[A-Z]+$ //匹配由26个英文字母的大写组成的字符串
^[a-z]+$ //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$ //匹配由数字和26个英文字母组成的字符串
^w+$ //匹配由数字、26个英文字母或者下划线组成的字符串
评注:最基本也是最常用的一些表达式
匹配中文字符的正则表达式: [u4e00-u9fa5]
评注:匹配中文还真是个头疼的事,有了这个表达式就好办了
匹配双字节字符(包括汉字在内):[^x00-xff]
评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
匹配空白行的正则表达式: s*
评注:可以用来删除空白行
匹配HTML标记的正则表达式:<(S*?)[^>]*>.*?</1>|<.*? />
评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力
匹配首尾空白字符的正则表达式:^s*|s*$
评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式
匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
评注:表单验证时很实用
匹配网址URL的正则表达式:[a-zA-z]+://[^s]*
评注:网上流传的版本功能很有限,上面这个基本可以满足需求
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
评注:表单验证时很实用
匹配国内电话号码:d{3}-d{8}|d{4}-d{7}
评注:匹配形式如 0511-4405222 或 021-87888822
匹配腾讯QQ号:[1-9][0-9]{4,}
评注:腾讯QQ号从10000开始
匹配中国邮政编码:[1-9]d{5}(?!d)
评注:中国邮政编码为6位数字
匹配身份证:d{15}|d{18}
评注:中国的身份证为15位或18位
匹配ip地址:d+.d+.d+.d+
评注:提取ip地址时有用
匹配特定数字:
^[1-9]d*$ //匹配正整数
^-[1-9]d*$ //匹配负整数
^-?[1-9]d*$ //匹配整数
^[1-9]d*|0$ //匹配非负整数(正整数 + 0)
^-[1-9]d*|0$ //匹配非正整数(负整数 + 0)
^[1-9]d*.d*|0.d*[1-9]d*$ //匹配正浮点数
^-([1-9]d*.d*|0.d*[1-9]d*)$ //匹配负浮点数
^-?([1-9]d*.d*|0.d*[1-9]d*|0?.0+|0)$ //匹配浮点数
^[1-9]d*.d*|0.d*[1-9]d*|0?.0+|0$ //匹配非负浮点数(正浮点数 + 0)
^(-([1-9]d*.d*|0.d*[1-9]d*))|0?.0+|0$ //匹配非正浮点数(负浮点数 + 0)
评注:处理大量数据时有用,具体应用时注意修正
匹配特定字符串:
^[A-Za-z]+$ //匹配由26个英文字母组成的字符串
^[A-Z]+$ //匹配由26个英文字母的大写组成的字符串
^[a-z]+$ //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$ //匹配由数字和26个英文字母组成的字符串
^w+$ //匹配由数字、26个英文字母或者下划线组成的字符串
评注:最基本也是最常用的一些表达式
大家都知道,rename()函数可以对文件或目录进行重命名的操作。其实它还可以做很多事情。
熟悉unix的朋友应该知道shell命令mv,它相当与win32的移动,而且移动的同时可进行重命名。我发现,php的rename()函数就相当于mv,它不仅仅只有简单的重命名的功能,同样可以改变文件甚至整个目录的路径。
例如:
$oldpath ----文件或目录原来路径
$newpath ----新定义路径
那么 rename($oldpath,$newpath)就可以完成文件/目录移动的操作
win32和unix的php4版本都支持这个功能。
另外,好象php4的win32版取消了unlink()函数。那么还可以巧用rename()函数来完成删除的操作,例如:
$path ---- 文件或目录路径
$tmp ---- tmp目录(/tmp)
用rename($path,$tmp) 将文件移动到tmp目录.
熟悉unix的朋友应该知道shell命令mv,它相当与win32的移动,而且移动的同时可进行重命名。我发现,php的rename()函数就相当于mv,它不仅仅只有简单的重命名的功能,同样可以改变文件甚至整个目录的路径。
例如:
$oldpath ----文件或目录原来路径
$newpath ----新定义路径
那么 rename($oldpath,$newpath)就可以完成文件/目录移动的操作
win32和unix的php4版本都支持这个功能。
另外,好象php4的win32版取消了unlink()函数。那么还可以巧用rename()函数来完成删除的操作,例如:
$path ---- 文件或目录路径
$tmp ---- tmp目录(/tmp)
用rename($path,$tmp) 将文件移动到tmp目录.
在《IP地址->地理位置转换的测评》一文中提到用ip2addr函数直接读取IP数据库文件是效率最高的,相比用MySQL数据库存储IP数据,用SQL查询是效率最低的。但是IP数据库文件QQWry.dat是GB2312编码的。现在我需要UTF-8编码的地理位置结果。如果用MySQL方法,可以在数据存入数据库时就转换为UTF-8编码,一劳永逸。但是QQWry.dat文件又无法修改,只能把ip2addr函数的输出结果再进行动态转换。
动态转换GB->UTF-8编码至少有四种方法:
用PHP的iconv扩展转换
用PHP的mb_string扩展转换
用对换表转换,对换表存储在MySQL数据库中
用对换表转换,对换表存储在文本文件中
前两种方法要服务器作了相应设置(编译安装了相应扩展)才能使用。我的虚拟主机没有这两个扩展,只好考虑后两种方法。前两个方法本文也不进行测评。
测评程序如下(func_ip.php参见《IP地址->地理位置转换的测评》一文):
[code]<?php
require_once ("func_ip.php");
function u2utf8($c) {
$str = "";
if ($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);
}
return $str;
}
function GB2UTF8_SQL($strGB) {
if (!trim($strGB)) return $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 = "SELECT code_unicode FROM nnstats_gb_unicode
WHERE code_gb = ".$intGB." LIMIT 1"
;
$resResult = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
else $strRet .= "??";
$i++;
} else {
$strRet .= $strGB{$i};
}
}
return $strRet;
}
function GB2UTF8_FILE($strGB) {
if (!trim($strGB)) return $strGB;
$arrLines = file("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]);
else $strRet .= "??";
$i++;
} else {
$strRet .= $strGB{$i};
}
}
return $strRet;
}
function EncodeIp($strDotquadIp) {
$arrIpSep = explode('.', $strDotquadIp);
if (count($arrIpSep) != 4) return 0;
$intIp = 0;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
return $intIp;
//return sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
function GetMicroTime() {
list($msec, $sec) = explode(" ", microtime());
return ((double)$msec + (double)$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("test");
// 测评MySQL查询的编码转换
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// 测评结束并输出结果
echo $dblTimeDuration; echo " ";
// 测评文本文件查询的编码转换
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// 测评结束并输出结果
echo $dblTimeDuration; echo " ";
?>[/code]
测评两次结果(精确到3位小数,单位是秒):
MySQL查询转换:0.112
文本查询转换:10.590
MySQL查询转换:0.099
文本查询转换:10.623
可见这次是MySQL方法遥遥领先于文件查询法。但是现在还不急于使用MySQL方法,因为文本文件方法之所以如此耗时,主要因为它每次转换都要把整个gb_unicode.txt读入内存,而gb_unicode.txt又是文本文件,格式如下:
文本文件效率较低,于是考虑把文本文件转换为二进制文件,然后用折半法查找这个文件,而不需要把整个文件读入内存。文件格式为:文件头2字节,存储记录数;接着一条接一条记录存入文件,每条记录4字节,前2字节对应GB代码,后2字节对应Unicode代码。转换程序如下:
执行程序后就获得了二进制的GB->Unicode对照表gbu.dat,并且数据记录按GB代码排了序,便于折半法查找。使用gbu.dat进行转码的函数如下:
把其加到原来的测评程序,对三种方法同时测评2次得到数据(精确到3位小数,单位:秒):
MySQL方法:0.125
文本文件方法:10.873
二进制文件折半法:0.106
MySQL方法:0.102
文本文件方法:10.677
二进制文件折半法:0.092
可见二进制文件折半法还比MySQL法略有优势。但是上述测评都是对短的地理位置进行转码,如果对较长的文本转码又如何呢?我找来5个Blog的RSS 2.0文件,都是GB2312编码。测评三种方法对5个文件编码耗费的时间,2次测量数据如下(精确到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编码的动态转换,如果每次转换的文本很小,适宜用二进制文件结合折半法转换;如果每次转换的文本较大,适宜用文本文件存储转码对照表,并在转换前一次性把对照表读入内存。
动态转换GB->UTF-8编码至少有四种方法:
用PHP的iconv扩展转换
用PHP的mb_string扩展转换
用对换表转换,对换表存储在MySQL数据库中
用对换表转换,对换表存储在文本文件中
前两种方法要服务器作了相应设置(编译安装了相应扩展)才能使用。我的虚拟主机没有这两个扩展,只好考虑后两种方法。前两个方法本文也不进行测评。
测评程序如下(func_ip.php参见《IP地址->地理位置转换的测评》一文):
[code]<?php
require_once ("func_ip.php");
function u2utf8($c) {
$str = "";
if ($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);
}
return $str;
}
function GB2UTF8_SQL($strGB) {
if (!trim($strGB)) return $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 = "SELECT code_unicode FROM nnstats_gb_unicode
WHERE code_gb = ".$intGB." LIMIT 1"
;
$resResult = mysql_query($strSql);
if ($arrCode = mysql_fetch_array($resResult)) $strRet .= u2utf8($arrCode["code_unicode"]);
else $strRet .= "??";
$i++;
} else {
$strRet .= $strGB{$i};
}
}
return $strRet;
}
function GB2UTF8_FILE($strGB) {
if (!trim($strGB)) return $strGB;
$arrLines = file("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]);
else $strRet .= "??";
$i++;
} else {
$strRet .= $strGB{$i};
}
}
return $strRet;
}
function EncodeIp($strDotquadIp) {
$arrIpSep = explode('.', $strDotquadIp);
if (count($arrIpSep) != 4) return 0;
$intIp = 0;
foreach ($arrIpSep as $k => $v) $intIp += (int)$v * pow(256, 3 - $k);
return $intIp;
//return sprintf('%02x%02x%02x%02x', $arrIpSep[0], $arrIpSep[1], $arrIpSep[2], $arrIpSep[3]);
}
function GetMicroTime() {
list($msec, $sec) = explode(" ", microtime());
return ((double)$msec + (double)$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("test");
// 测评MySQL查询的编码转换
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_SQL($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_SQL($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// 测评结束并输出结果
echo $dblTimeDuration; echo " ";
// 测评文本文件查询的编码转换
$dblTimeStart = GetMicroTime();
for ($i = 0; $i < 100; $i++) {
$strUTF8Region = GB2UTF8_FILE($arrAddr[$i]["region"]);
$strUTF8Address = GB2UTF8_FILE($arrAddr[$i]["address"]);
}
$dblTimeDuration = GetMicroTime() - $dblTimeStart;
// 测评结束并输出结果
echo $dblTimeDuration; echo " ";
?>[/code]
测评两次结果(精确到3位小数,单位是秒):
MySQL查询转换:0.112
文本查询转换:10.590
MySQL查询转换:0.099
文本查询转换:10.623
可见这次是MySQL方法遥遥领先于文件查询法。但是现在还不急于使用MySQL方法,因为文本文件方法之所以如此耗时,主要因为它每次转换都要把整个gb_unicode.txt读入内存,而gb_unicode.txt又是文本文件,格式如下:
0x2121 0x3000 # IDEOGRAPHIC SPACE
0x2122 0x3001 # IDEOGRAPHIC COMMA
0x2123 0x3002 # IDEOGRAPHIC FULL STOP
0x2124 0x30FB # KATAKANA MIDDLE DOT
0x2125 0x02C9 # MODIFIER LETTER MACRON (Mandarin Chinese first tone)
……
0x552A 0x6458 # <CJK>
0x552B 0x658B # <CJK>
0x552C 0x5B85 # <CJK>
0x552D 0x7A84 # <CJK>
……
0x777B 0x9F37 # <CJK>
0x777C 0x9F3D # <CJK>
0x777D 0x9F3E # <CJK>
0x777E 0x9F44 # <CJK>
0x2122 0x3001 # IDEOGRAPHIC COMMA
0x2123 0x3002 # IDEOGRAPHIC FULL STOP
0x2124 0x30FB # KATAKANA MIDDLE DOT
0x2125 0x02C9 # MODIFIER LETTER MACRON (Mandarin Chinese first tone)
……
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 as $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 as $k => $v) {
$strData = chr($k % 256) . chr(floor($k / 256)) . chr($v % 256) . chr(floor($v / 256));
fwrite($fileGBU, $strData);
}
fclose($fileGBU);
?>
$arrLines = file("gb_unicode.txt");
foreach ($arrLines as $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 as $k => $v) {
$strData = chr($k % 256) . chr(floor($k / 256)) . chr($v % 256) . chr(floor($v / 256));
fwrite($fileGBU, $strData);
}
fclose($fileGBU);
?>
执行程序后就获得了二进制的GB->Unicode对照表gbu.dat,并且数据记录按GB代码排了序,便于折半法查找。使用gbu.dat进行转码的函数如下:
function GB2UTF8_FILE1($strGB) {
if (!trim($strGB)) return $strGB;
$fileGBU = fopen("gbu.dat", "rb");
$strBuf = fread($fileGBU, 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;
$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});
if ($intGB == $intCode) {
$intStart = $intMid;
break;
}
if ($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});
if ($intGB == $intCode) {
$strBuf = fread($fileGBU, 2);
$intCodeU = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet .= u2utf8($intCodeU);
} else {
$strRet .= "??";
}
$i++;
} else {
$strRet .= $strGB{$i};
}
}
return $strRet;
}
if (!trim($strGB)) return $strGB;
$fileGBU = fopen("gbu.dat", "rb");
$strBuf = fread($fileGBU, 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;
$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});
if ($intGB == $intCode) {
$intStart = $intMid;
break;
}
if ($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});
if ($intGB == $intCode) {
$strBuf = fread($fileGBU, 2);
$intCodeU = ord($strBuf{0}) + 256 * ord($strBuf{1});
$strRet .= u2utf8($intCodeU);
} else {
$strRet .= "??";
}
$i++;
} else {
$strRet .= $strGB{$i};
}
}
return $strRet;
}
把其加到原来的测评程序,对三种方法同时测评2次得到数据(精确到3位小数,单位:秒):
MySQL方法:0.125
文本文件方法:10.873
二进制文件折半法:0.106
MySQL方法:0.102
文本文件方法:10.677
二进制文件折半法:0.092
可见二进制文件折半法还比MySQL法略有优势。但是上述测评都是对短的地理位置进行转码,如果对较长的文本转码又如何呢?我找来5个Blog的RSS 2.0文件,都是GB2312编码。测评三种方法对5个文件编码耗费的时间,2次测量数据如下(精确到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编码的动态转换,如果每次转换的文本很小,适宜用二进制文件结合折半法转换;如果每次转换的文本较大,适宜用文本文件存储转码对照表,并在转换前一次性把对照表读入内存。
(1)、back_log:
要求 MySQL 能有的连接数量。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用,然后主线程花些时间(尽管很短)检查连接并且启动一个新线程。
back_log 值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。只有如果期望在一个短时间内有很多连接,你需要增加它,换句话说,这值对到来的TCP/IP连接的侦听队列的大小。你的操作系统在这个队列大小上有它自己的限制。试图设定back_log高于你的操作系统的限制将是无效的。
当你观察你的主机进程列表,发现大量 264084 | unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时,就要加大 back_log 的值了。默认数值是50,我把它改为500。
(2)、interactive_timeout:
服务器在关闭它前在一个交互连接上等待行动的秒数。一个交互的客户被定义为对 mysql_real_connect()使用 CLIENT_INTERACTIVE 选项的客户。 默认数值是28800,我把它改为7200。
(3)、key_buffer_size:
索引块是缓冲的并且被所有的线程共享。key_buffer_size是用于索引块的缓冲区大小,增加它可得到更好处理的索引(对所有读和多重写),到你能负担得起那样多。如果你使它太大,系统将开始换页并且真的变慢了。默认数值是8388600(8M),我的MySQL主机有2GB内存,所以我把它改为 402649088(400MB)。
(4)、max_connections:
允许的同时客户的数量。增加该值增加 mysqld 要求的文件描述符的数量。这个数字应该增加,否则,你将经常看到 Too many connections 错误。 默认数值是100,我把它改为1024 。
(5)、record_buffer:
每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,你可能想要增加该值。默认数值是131072(128K),我把它改为16773120 (16M)
(6)、sort_buffer:
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速ORDER BY或GROUP BY操作。默认数值是2097144(2M),我把它改为 16777208 (16M)。
(7)、table_cache:
为所有线程打开表的数量。增加该值能增加mysqld要求的文件描述符的数量。MySQL对每个唯一打开的表需要2个文件描述符。默认数值是64,我把它改为512。
(8)、thread_cache_size:
可以复用的保存在中的线程的数量。如果有,新的线程从缓存中取得,当断开连接的时候如果有空间,客户的线置在缓存中。如果有很多新的线程,为了提高性能可以这个变量值。通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。我把它设置为 80。
(9)mysql的搜索功能
用mysql进行搜索,目的是能不分大小写,又能用中文进行搜索
只需起动mysqld时指定 --default-character-set=gb2312
(10)、wait_timeout:
服务器在关闭它之前在一个连接上等待行动的秒数。 默认数值是28800,我把它改为7200。
注:参数的调整可以通过修改 /etc/my.cnf 文件并重启 MySQL 实现。这是一个比较谨慎的工作,上面的结果也仅仅是我的一些看法,你可以根据你自己主机的硬件情况(特别是内存大小)进一步修改。
4.1版本略做修改 依次搜索就可以 4.1的配置文件是MYSQL目录下的my.ini
要求 MySQL 能有的连接数量。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用,然后主线程花些时间(尽管很短)检查连接并且启动一个新线程。
back_log 值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。只有如果期望在一个短时间内有很多连接,你需要增加它,换句话说,这值对到来的TCP/IP连接的侦听队列的大小。你的操作系统在这个队列大小上有它自己的限制。试图设定back_log高于你的操作系统的限制将是无效的。
当你观察你的主机进程列表,发现大量 264084 | unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时,就要加大 back_log 的值了。默认数值是50,我把它改为500。
(2)、interactive_timeout:
服务器在关闭它前在一个交互连接上等待行动的秒数。一个交互的客户被定义为对 mysql_real_connect()使用 CLIENT_INTERACTIVE 选项的客户。 默认数值是28800,我把它改为7200。
(3)、key_buffer_size:
索引块是缓冲的并且被所有的线程共享。key_buffer_size是用于索引块的缓冲区大小,增加它可得到更好处理的索引(对所有读和多重写),到你能负担得起那样多。如果你使它太大,系统将开始换页并且真的变慢了。默认数值是8388600(8M),我的MySQL主机有2GB内存,所以我把它改为 402649088(400MB)。
(4)、max_connections:
允许的同时客户的数量。增加该值增加 mysqld 要求的文件描述符的数量。这个数字应该增加,否则,你将经常看到 Too many connections 错误。 默认数值是100,我把它改为1024 。
(5)、record_buffer:
每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,你可能想要增加该值。默认数值是131072(128K),我把它改为16773120 (16M)
(6)、sort_buffer:
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速ORDER BY或GROUP BY操作。默认数值是2097144(2M),我把它改为 16777208 (16M)。
(7)、table_cache:
为所有线程打开表的数量。增加该值能增加mysqld要求的文件描述符的数量。MySQL对每个唯一打开的表需要2个文件描述符。默认数值是64,我把它改为512。
(8)、thread_cache_size:
可以复用的保存在中的线程的数量。如果有,新的线程从缓存中取得,当断开连接的时候如果有空间,客户的线置在缓存中。如果有很多新的线程,为了提高性能可以这个变量值。通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。我把它设置为 80。
(9)mysql的搜索功能
用mysql进行搜索,目的是能不分大小写,又能用中文进行搜索
只需起动mysqld时指定 --default-character-set=gb2312
(10)、wait_timeout:
服务器在关闭它之前在一个连接上等待行动的秒数。 默认数值是28800,我把它改为7200。
注:参数的调整可以通过修改 /etc/my.cnf 文件并重启 MySQL 实现。这是一个比较谨慎的工作,上面的结果也仅仅是我的一些看法,你可以根据你自己主机的硬件情况(特别是内存大小)进一步修改。
4.1版本略做修改 依次搜索就可以 4.1的配置文件是MYSQL目录下的my.ini
数据库存储中的数据类型与大小各异。有些地方只存储数字类型,有些只存储文本类型,有些二者而兼之。而很多数据库支持各种专用类型:日期和时间类型,二进制字符类型以及布尔类型。
选择数据类型与数据相匹配是数据库设计中最为重要的部分,因为这种类型将会影响到RDBMS的效率与性能。所以,对RDBMS的数据类型选择应给予足够的重视。
这就是编写本文的主要目的。表A将列举了MySQL支持的绝大部分重要的数据类型,而MySQL是当前最为流行的免费RDBMS。文中还描述了何时与如何使用这些数据类型。这将有助于建立一个数据库的合理设计。
1.超长的页面下载时间.
如果页面下载时间超过30秒,很难有用户会喜欢你的网站.
2.无限制的使用flash及图片
无可否认,适当的用一些图片及flash,可以增加网站的生动性,增加视觉冲击力.但无限制的使用flash及图片.会造成页面文件超大,占用浏览者的cpu资源,并且不利于页面更新及搜索引擎对网站的抓取。
3。网站页面过长.
你认为有多少浏览都有兴趣看你网页中最下面的内容?不要拿自己来作比喻,因为99%以上的人才刚学会上网.
在王建硕的一篇文章中提到."1995年JakobNeilson做的互联网用户调查,美国的用户在1994年的时候,只有10%的用户会拖动浏览器右边的滚动条,而绝大多数,90%的用户,打开一个网站,只看浏览第一屏看到的内容,就以为看到了全部,而不会向下滚动。"
现在中国也有这样的人.而且为数不少.
在一个网站的首页,能看到第三屏内容的人只有10%以下一个过长的网站很容易引起浏览者的视觉疲劳,更何况大部分浏览者很有可能已经被前
两屏的内容吸引到别的页面去了.
4.不友好的导航.
不友好的导航是最影响用户操作的,不能让用记很方便的找到自己想到的内容.用户来到一个页面不知如何返回上一页,不知道当前页面是在哪个栏目下的.这样的网站很可能用户来了一次就不会再来了.
5.过期的信息
很久不更新的信息,很容易让浏览者感到反感,而且在心中也会对你这个网站的品牌形象大打折扣.
6.死连接或连接错误.
这个就不说了,这是最基本的错误,但是好些还有这样的错误,包括新浪这种大网站.
7.孤立的页面.
用户不知用什么方法返回首页.这种情况往往是出现在信息提示页或内容调查的结果页上.
8.页面没有视觉差异.
页面没有视觉差异,页面设计很"平"缺少"层次感",缺少视觉冲击力和亮点.或者视觉冲击力突出的并不是网站的主体内容.这是没有经验的设计师设计大型网站时最容易犯的错误.
把一大堆信息铺天盖地的展示到浏览者的眼前,你认为他会记住多少?页面设计要吸引并引导浏览者来观看你想推广的内容或产品.当然对网页"层次感"的设计平不是简单的用一些纯度高的颜色来实现,要根据页面的环境及周边元素综合考虑.就象在一个黑板中画一个白点很明显,但在一个白板上画一个白点就看不清楚了.
9.链连没有标准的表现形式.
现在很多刚上网站的人还只认为有带下画线的文字才是链接.网站要有统一标准的链接表现形式,并且要和没有连接的文字有区别.要让浏览者很方便的认出哪些是连接的文字.
如果是图片加的连接要在图片下标出"点击图片见大图",图片一定要加"alt"属性.
"更多"要用中文写最好不要"more"或者标点符号代替.
10.过多的运用新技术.
所谓新技术,就是只有少数人掌握的技术,虽然有可能他的视觉效果很好,功能很强大,但过多的运用新技术,就意味着你准备抛弃99%的用户.
11.缺少互动的内容.
缺少互动的内容,缺少网友的参与.不能让网友表达情感和思想,注定这个网站只是个死网站.
12.过复杂的文件目录及文件名.
过复杂的文件目录及文件名,不利用搜索引擎对页面的拾取,并且也不利于浏览者的记忆.几乎100%的人输入网址时会用到IE的缓存.目录和文件过于复杂,排在IE缓存很靠下的地方,你当然被第二次访问的机率小的多.
不要说什么用收藏夹.你以为会有超过一半的人会用收藏夹吗?而且象我这种收藏夹过于庞大的人,想在收藏夹里找一个网址也是比较不容易的.
13.使用框架.
不建议使用框架.不为什么,就因为搜索引荐不喜欢.连google的广告计划中,对有框架的代码都是单独的.
14.恶意插件,恶意弹出窗口.
15.页面中不要过多的用"_blank"。
过多的弹出新窗口,会大量占用计算机的资源.影响浏览者的浏览速度
如果页面下载时间超过30秒,很难有用户会喜欢你的网站.
2.无限制的使用flash及图片
无可否认,适当的用一些图片及flash,可以增加网站的生动性,增加视觉冲击力.但无限制的使用flash及图片.会造成页面文件超大,占用浏览者的cpu资源,并且不利于页面更新及搜索引擎对网站的抓取。
3。网站页面过长.
你认为有多少浏览都有兴趣看你网页中最下面的内容?不要拿自己来作比喻,因为99%以上的人才刚学会上网.
在王建硕的一篇文章中提到."1995年JakobNeilson做的互联网用户调查,美国的用户在1994年的时候,只有10%的用户会拖动浏览器右边的滚动条,而绝大多数,90%的用户,打开一个网站,只看浏览第一屏看到的内容,就以为看到了全部,而不会向下滚动。"
现在中国也有这样的人.而且为数不少.
在一个网站的首页,能看到第三屏内容的人只有10%以下一个过长的网站很容易引起浏览者的视觉疲劳,更何况大部分浏览者很有可能已经被前
两屏的内容吸引到别的页面去了.
4.不友好的导航.
不友好的导航是最影响用户操作的,不能让用记很方便的找到自己想到的内容.用户来到一个页面不知如何返回上一页,不知道当前页面是在哪个栏目下的.这样的网站很可能用户来了一次就不会再来了.
5.过期的信息
很久不更新的信息,很容易让浏览者感到反感,而且在心中也会对你这个网站的品牌形象大打折扣.
6.死连接或连接错误.
这个就不说了,这是最基本的错误,但是好些还有这样的错误,包括新浪这种大网站.
7.孤立的页面.
用户不知用什么方法返回首页.这种情况往往是出现在信息提示页或内容调查的结果页上.
8.页面没有视觉差异.
页面没有视觉差异,页面设计很"平"缺少"层次感",缺少视觉冲击力和亮点.或者视觉冲击力突出的并不是网站的主体内容.这是没有经验的设计师设计大型网站时最容易犯的错误.
把一大堆信息铺天盖地的展示到浏览者的眼前,你认为他会记住多少?页面设计要吸引并引导浏览者来观看你想推广的内容或产品.当然对网页"层次感"的设计平不是简单的用一些纯度高的颜色来实现,要根据页面的环境及周边元素综合考虑.就象在一个黑板中画一个白点很明显,但在一个白板上画一个白点就看不清楚了.
9.链连没有标准的表现形式.
现在很多刚上网站的人还只认为有带下画线的文字才是链接.网站要有统一标准的链接表现形式,并且要和没有连接的文字有区别.要让浏览者很方便的认出哪些是连接的文字.
如果是图片加的连接要在图片下标出"点击图片见大图",图片一定要加"alt"属性.
"更多"要用中文写最好不要"more"或者标点符号代替.
10.过多的运用新技术.
所谓新技术,就是只有少数人掌握的技术,虽然有可能他的视觉效果很好,功能很强大,但过多的运用新技术,就意味着你准备抛弃99%的用户.
11.缺少互动的内容.
缺少互动的内容,缺少网友的参与.不能让网友表达情感和思想,注定这个网站只是个死网站.
12.过复杂的文件目录及文件名.
过复杂的文件目录及文件名,不利用搜索引擎对页面的拾取,并且也不利于浏览者的记忆.几乎100%的人输入网址时会用到IE的缓存.目录和文件过于复杂,排在IE缓存很靠下的地方,你当然被第二次访问的机率小的多.
不要说什么用收藏夹.你以为会有超过一半的人会用收藏夹吗?而且象我这种收藏夹过于庞大的人,想在收藏夹里找一个网址也是比较不容易的.
13.使用框架.
不建议使用框架.不为什么,就因为搜索引荐不喜欢.连google的广告计划中,对有框架的代码都是单独的.
14.恶意插件,恶意弹出窗口.
15.页面中不要过多的用"_blank"。
过多的弹出新窗口,会大量占用计算机的资源.影响浏览者的浏览速度
转载也~~
最近在开发社区程序,收集和自己想了一些能提高用户体验的社区设计理念,拿出来和大家讨论讨论.
1,大型社区导航的设计三点考虑:
1 “随时”出现在用户手边;
2 尽量减少对页面的占用 ;
3 给用户良好的“位置感”;
现在的一般的大型社区都是采用左侧可隐藏式的框架设计来实现的。
关于位置感觉,在导航拦的体现就是 当前浏览的拦目要突出显示
2,"恢复上一次提交"功能
http协议是无连接的,由于网络不稳定导致用户发表失败,应该提供可恢复的手段。
3,完全可定制的可见即可得编辑器 ,分三种情况
1,用户可自己选择自己喜欢的编辑器。比如freetextbox,....
2,用户可定制编辑器的某些功能,比如有些用户喜欢发表图片,发表视频,但有些确喜欢
插入代码等,应该提供可选的定制功能,以避免过多的功能影响使用的方便性能和提高
加载速度。
3,具备一定的智能,比如进入贴图区,贴图功能自动出现,用户进入程序设计区,自动出现插入
代码功能,等等。
4,可匿名回复帖子。
有很多网站提供这个功能,也有很多网站不提供这个功能。我认为是否提供这个功能是判断
一个社区是否体贴用户的一个标志。时刻记着,引导用户注册而非强迫用户注册。前者带来
的注册用户对社区的认同感和粘性将更强。
5,分页的考虑
1,用户应该可以随时选择 每页显示20条,还是40条,或其他指定的任何分页值。这个可用于
列表页,也可用于内容页的情况。
2,尽量用数字表示页码,而不是上页/下页的样式。baidu,google的设计的就不错。
6,站内短消息功能
1,容量应该是有限度的。比如最多保存20条等。站内短消息是用来即时交流和通知信息
不是用来存储的。以提醒用户及时处理自己的信息。同时也可清理那些N年不来一次的
用户的短消息,以避免浪费系统资源。
2,可导出短消息。
3,草稿箱功能。满足两种用户需要:先写好,想想是否要发;写完了,突然不想发,但不肯定
将来要不要发,提供保存功能。
4,可设置不接收某些用户的信息,或者不接受所有用户的信息。
7,贴心的搜索功能
一般的老用户在你的社区呆久了,自然就会添加了很多好友,收藏了很多帖子。也可能发表
了很多帖子,应该提供精确定向的搜索功能,可搜索自己的发表的或收藏的帖子,好友的发
表的帖子的等等。这个功能大多的社区都没提供。
8,在用户写作区(编辑器)的某个合适的地方提供搜索框
可用google或baidu的搜索框。有些用户喜欢在线写作,偶尔需要搜索引用些资料。这样会
给用户提供一定的方便。还有可能为网站增加收入。但要注意,以不影响用户写作为前提。
这个功能特别适合技术类的blog。
9,智能化的推荐帖
有些社区在精华帖之外,还有个推荐帖的概念,不过我认为推荐帖不是由网站决定的,而应该
根据用户以往的浏览记录,收藏记录。用户信息的爱好等信息智能判断提取用户最有可能喜欢的
帖子做为推荐帖子,当然也可能考虑帖子的多种考虑因素,增加推荐成功率。
10,楼主/博主的回复“突出显示”
这个在某些时候会很有用。不过我觉得对blog特别有用。因为你阅读博主blog的文章,对博主
的回复自然感兴趣,对论坛的意思不是特别大。
最近在开发社区程序,收集和自己想了一些能提高用户体验的社区设计理念,拿出来和大家讨论讨论.
1,大型社区导航的设计三点考虑:
1 “随时”出现在用户手边;
2 尽量减少对页面的占用 ;
3 给用户良好的“位置感”;
现在的一般的大型社区都是采用左侧可隐藏式的框架设计来实现的。
关于位置感觉,在导航拦的体现就是 当前浏览的拦目要突出显示
2,"恢复上一次提交"功能
http协议是无连接的,由于网络不稳定导致用户发表失败,应该提供可恢复的手段。
3,完全可定制的可见即可得编辑器 ,分三种情况
1,用户可自己选择自己喜欢的编辑器。比如freetextbox,....
2,用户可定制编辑器的某些功能,比如有些用户喜欢发表图片,发表视频,但有些确喜欢
插入代码等,应该提供可选的定制功能,以避免过多的功能影响使用的方便性能和提高
加载速度。
3,具备一定的智能,比如进入贴图区,贴图功能自动出现,用户进入程序设计区,自动出现插入
代码功能,等等。
4,可匿名回复帖子。
有很多网站提供这个功能,也有很多网站不提供这个功能。我认为是否提供这个功能是判断
一个社区是否体贴用户的一个标志。时刻记着,引导用户注册而非强迫用户注册。前者带来
的注册用户对社区的认同感和粘性将更强。
5,分页的考虑
1,用户应该可以随时选择 每页显示20条,还是40条,或其他指定的任何分页值。这个可用于
列表页,也可用于内容页的情况。
2,尽量用数字表示页码,而不是上页/下页的样式。baidu,google的设计的就不错。
6,站内短消息功能
1,容量应该是有限度的。比如最多保存20条等。站内短消息是用来即时交流和通知信息
不是用来存储的。以提醒用户及时处理自己的信息。同时也可清理那些N年不来一次的
用户的短消息,以避免浪费系统资源。
2,可导出短消息。
3,草稿箱功能。满足两种用户需要:先写好,想想是否要发;写完了,突然不想发,但不肯定
将来要不要发,提供保存功能。
4,可设置不接收某些用户的信息,或者不接受所有用户的信息。
7,贴心的搜索功能
一般的老用户在你的社区呆久了,自然就会添加了很多好友,收藏了很多帖子。也可能发表
了很多帖子,应该提供精确定向的搜索功能,可搜索自己的发表的或收藏的帖子,好友的发
表的帖子的等等。这个功能大多的社区都没提供。
8,在用户写作区(编辑器)的某个合适的地方提供搜索框
可用google或baidu的搜索框。有些用户喜欢在线写作,偶尔需要搜索引用些资料。这样会
给用户提供一定的方便。还有可能为网站增加收入。但要注意,以不影响用户写作为前提。
这个功能特别适合技术类的blog。
9,智能化的推荐帖
有些社区在精华帖之外,还有个推荐帖的概念,不过我认为推荐帖不是由网站决定的,而应该
根据用户以往的浏览记录,收藏记录。用户信息的爱好等信息智能判断提取用户最有可能喜欢的
帖子做为推荐帖子,当然也可能考虑帖子的多种考虑因素,增加推荐成功率。
10,楼主/博主的回复“突出显示”
这个在某些时候会很有用。不过我觉得对blog特别有用。因为你阅读博主blog的文章,对博主
的回复自然感兴趣,对论坛的意思不是特别大。






