2009年2月10日星期二

纯真IP地址查询库

以下是PHP版本,有2个问题需要注意: 1. 每次加载QQWry.Dat文件对内存占用过大,不可接受。 2.由于IP和城市对应关系混乱,所以返回的为某具体地区,比如”复旦某宿舍”。

《纯真IP数据库格式详解》
链接一:http://blog.csdn.net/heiyeshuwu/archive/2006/05/12/725675.aspx
链接二:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
纯真IP数据库官网:http://www.cz88.net/ip/
纯真IP数据库下载:http://update.cz88.net/soft/qqwry.rar

以下函数conrvertip()位于 Discuz!5_GBK/upload/include/misc.func.php 路径中,有兴趣可以具体去阅读分析。(下面代码我做了简单的修改,更便于阅读,核心没有修改)


//===================================
//
// 功能:IP地址获取真实地址函数
// 参数:$ip - IP地址
// 作者:[Discuz!] (C) Comsenz Inc.
//
//===================================
function convertip($ip) {
//IP数据文件路径
$dat_path = 'QQWry.Dat';

//检查IP地址
if(!preg_match("/^d{1,3}.d{1,3}.d{1,3}.d{1,3}$/", $ip)) {
return 'IP Address Error';
}
//打开IP数据文件
if(!$fd = @fopen($dat_path, 'rb')){
return 'IP date file not exists or access denied';
}

//分解IP进行运算,得出整形数
$ip = explode('.', $ip);
$ipNum = $ip[0] * 16777216 + $ip[1] * 65536 + $ip[2] * 256 + $ip[3];

//获取IP数据索引开始和结束位置
$DataBegin = fread($fd, 4);
$DataEnd = fread($fd, 4);
$ipbegin = implode('', unpack('L', $DataBegin));
if($ipbegin < 0) $ipbegin += pow(2, 32);
$ipend = implode('', unpack('L', $DataEnd));
if($ipend < 0) $ipend += pow(2, 32);
$ipAllNum = ($ipend - $ipbegin) / 7 + 1;

$BeginNum = 0;
$EndNum = $ipAllNum;

//使用二分查找法从索引记录中搜索匹配的IP记录
while($ip1num>$ipNum || $ip2num<$ipNum) {
$Middle= intval(($EndNum + $BeginNum) / 2);

//偏移指针到索引位置读取4个字节
fseek($fd, $ipbegin + 7 * $Middle);
$ipData1 = fread($fd, 4);
if(strlen($ipData1) < 4) {
fclose($fd);
return 'System Error';
}
//提取出来的数据转换成长整形,如果数据是负数则加上2的32次幂
$ip1num = implode('', unpack('L', $ipData1));
if($ip1num < 0) $ip1num += pow(2, 32);

//提取的长整型数大于我们IP地址则修改结束位置进行下一次循环
if($ip1num > $ipNum) {
$EndNum = $Middle;
continue;
}

//取完上一个索引后取下一个索引
$DataSeek = fread($fd, 3);
if(strlen($DataSeek) < 3) {
fclose($fd);
return 'System Error';
}
$DataSeek = implode('', unpack('L', $DataSeek.chr(0)));
fseek($fd, $DataSeek);
$ipData2 = fread($fd, 4);
if(strlen($ipData2) < 4) {
fclose($fd);
return 'System Error';
}
$ip2num = implode('', unpack('L', $ipData2));
if($ip2num < 0) $ip2num += pow(2, 32);

//没找到提示未知
if($ip2num < $ipNum) {
if($Middle == $BeginNum) {
fclose($fd);
return 'Unknown';
}
$BeginNum = $Middle;
}
}


以下是Java版实现:

/**
* 给定一个ip国家地区记录的偏移,返回一个IPLocation结构
* @param offset 国家记录的起始偏移
* @return IPLocation对象
*/
private IPLocation getIPLocation(long offset) {
try {
// 跳过4字节ip
ipFile.seek(offset + 4);
// 读取第一个字节判断是否标志字节
byte b = ipFile.readByte();
if(b == REDIRECT_MODE_1) {
// 读取国家偏移
long countryOffset = readLong3();
// 跳转至偏移处
ipFile.seek(countryOffset);
// 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向
b = ipFile.readByte();
if(b == REDIRECT_MODE_2) {
loc.country = readString(readLong3());
ipFile.seek(countryOffset + 4);
} else
loc.country = readString(countryOffset);
// 读取地区标志
loc.area = readArea(ipFile.getFilePointer());
} else if(b == REDIRECT_MODE_2) {
loc.country = readString(readLong3());
loc.area = readArea(offset + 8);
} else {
loc.country = readString(ipFile.getFilePointer() - 1);
loc.area = readArea(ipFile.getFilePointer());
}
return loc;
} catch (IOException e) {
return null;
}
}

/**
* 从offset偏移开始解析后面的字节,读出一个地区名
* @param offset 地区记录的起始偏移
* @return 地区名字符串
* @throws IOException 地区名字符串
*/
private String readArea(long offset) throws IOException {
ipFile.seek(offset);
byte b = ipFile.readByte();
if(b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2) {
long areaOffset = readLong3(offset + 1);
if(areaOffset == 0)
return LumaQQ.getString("unknown.area");
else
return readString(areaOffset);
} else
return readString(offset);
}

/**
* 从offset位置读取3个字节为一个long,因为java为big-endian格式,所以没办法
* 用了这么一个函数来做转换
* @param offset 整数的起始偏移
* @return 读取的long值,返回-1表示读取文件失败
*/
private long readLong3(long offset) {
long ret = 0;
try {
ipFile.seek(offset);
ipFile.readFully(b3);
ret |= (b3[0] & 0xFF);
ret |= ((b3[1] << 8) & 0xFF00);
ret |= ((b3[2] << 16) & 0xFF0000);
return ret;
} catch (IOException e) {
return -1;
}
}

/**
* 从当前位置读取3个字节转换成long
* @return 读取的long值,返回-1表示读取文件失败
*/
private long readLong3() {
long ret = 0;
try {
ipFile.readFully(b3);
ret |= (b3[0] & 0xFF);
ret |= ((b3[1] << 8) & 0xFF00);
ret |= ((b3[2] << 16) & 0xFF0000);
return ret;
} catch (IOException e) {
return -1;
}
}

/**
* 从offset偏移处读取一个以0结束的字符串
* @param offset 字符串起始偏移
* @return 读取的字符串,出错返回空字符串
*/
private String readString(long offset) {
try {
ipFile.seek(offset);
int i;
for(i = 0, buf[i] = ipFile.readByte(); buf[i] != 0; buf[++i] = ipFile.readByte());
if(i != 0)
return Utils.getString(buf, 0, i, "GBK");
} catch (IOException e) {
log.error(e.getMessage());
}
return "";
}

2009年2月8日星期日

php 简单 下载图片 程序


function getimg($remoteUrl, $savePath, $saveName){

$formats = array('.gif', '.jpg','.png','.bmp');
$extension = strrchr($remoteUrl, '.');
if(!in_array($extension, $formats)) return false;

$fileName = $savePath . $saveName . $extension;
$image = file_get_contents($remoteUrl);
$fp = @fopen($fileName, 'a');
fwrite($fp, $image);
fclose($fp);
return $fileName;
}

2008年12月30日星期二

IE6 重复字符的bug及解决方法

当多个浮动的元素彼此跟随,中间加注释的时候,最后一个浮动元素内的文本偶尔会复制到最下面去。学名Duplicate Characters Bug

程序代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="//www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>多了一只猪</title>
</head>
<body>
<div style="width:400px">
<div style="float:left"></div>
<!-- -->
<div style="float:right;width:400px">↓这就是多出来的那只猪</div>
</div>
</body>
</html>


可以通过以下的办法来解决:
   
1、不放置注释。最简单、最快捷的解决方法
   
2、注释不要放置于2个浮动的区块之间。
    
3、将文字区块包含在新的<div></div>之间,如:<div style="float:right;width:400px"><div>↓这就是多出来的那只猪</div>& lt;/div>。
   
4、去除文字区块的固定宽度,与3有相似之处。
   
5、有的人在猪后加一个<br />或者空格,但只是消除现象。
   
6、不要给浮动元素设置多宽度,使其不会到达包含元素的底部,或者对最后一个元素设置margin-right: -3px;或者更小。
   
7、注释可以这样写:<!--[if !IE]>Put your commentary in here...<![endif]-->

2008年12月13日星期六

SEO Analysis

Title Tag Analysis
The maximum number of characters we recommend for this Tag is 60.
Meta Description Tag Analysis
The maximum number of characters we recommend for this Tag is 150.
Meta Keywords Tag Analysis
Our recommended maximum number of characters for this tag is 874.

IMG ALT Tag Analysis
This section is available in the Member's area of our site when using the Web Page Analyzer. Many search engines consider this area when indexing and looking for keywords. Optimizing this area is quite easy when you know what search engines are looking for.
Keyword Density Analysis
This section is available in the Member's area of our site when using the Web Page Analyzer. This is probably one of the most important ranking factors of a Web page and is easily optimized with our Keyword Density Manipulizer™. Not all keyword density tools analyze this properly. In fact, virtually all those we have tested are wrong! Having the right tool is a must. Our tools are developed by full text search engine development engineers. Getting the wrong information can mean complete SEO failure.

Top Keyword Phrase Analysis
This section is available in the Member's area of our site when using the Web Page Analyzer. Two and three word phrases account for more than 55% of searches done using major search engines as published by Rackstat.com. Properly optimizing for this can net you a ton of additional traffic you may be missing out on. Our two and three word phrase analysis reports helps you to identify and capitalize on this easly.

2008年12月12日星期五

Mysql enum & int 使用情况 (mysql没有必要枚举整型)

写程序时如果用到数据库建议向sqlite看齐,sqlite可没有enum.

Enum: 多用于固定不变的一些数据内容
比如:周一,周二,周三,,,,,,周日

Tinyint: 多用于表示状态(0,1,2,3....), 性能上来说不比 enum差.

Enum存取数据内容时是整型数据,也会把数据转为字符串来处理.
Tinyint存取是整型就是整型.


另外:
数据表有字段: Status enum('0','1','2','3') default 0;

update table set `Status`=0; //这时查看数据内容结果 Status='';

如果Status tinyint default 0; //同样操作数据内容就是 Statsus=0;


-------------------------------------------------------------------

mysql没有必要枚举整型

有些人习惯地把一些状态表示为整型,在mysql存储这些整型时,却转化成字符串,存在了枚举型的列中。这是不必要的。因为在mysql存储这

些枚举字符串时是把这样字符串做一个序列(index),然后存储相应的index值,比如

Enum_value index
NULL NULL 如果NULL是被允许的话
'' 0 一切在在插入时,被视为非法值的字符串,插入的都为空
'a' 1
'b' 2
... ...

也就是说,我们在插入值时,是插入的'a','b'等等字符串,但Mysql真正存储是则是右边的index.如果真得左边的ENUM也为整型值,则是不可取的。因为:
If you store a number into an ENUM column, the number is treated as an index, and the value stored is the enumeration member

with that index. (However, this does not work with LOAD DATA, which treats all input as strings.) It's not advisable to

define an ENUM column with enumeration values that look like numbers, because this can easily become confusing. For example,

the following column has enumeration members with string values of '0', '1', and '2', but numeric index values of
1, 2, and 3.

ENUM的最多的个数也证明了这一点。
An enumeration can have a maximum of 65,535 elements.

同时,都知道程序的需求变化很快,尤其是WEB类的脚本程序。ENUM在表示状态时,也会面临着扩展状态所带来困扰。你必须改变表结构新的值被允许存入。

建议:表示状态类的最好用TINYINT。枚举不是不行,但至少它不是最好。

2008年12月10日星期三

Internet Explorer 中的 Cookie 的数字和大小限制

Microsoft Internet Explorer complies with following RFC 2109 recommended minimum limitations:

* 至少 300 Cookie
* 至少为 4096 字节每个 Cookie (如度量由组成 Cookie 非终端 Set-Cookie 标头的语法描述中字符的大小)
* 每个唯一的主机或域名称至少 20 个 Cookie

注意 these recommended minimum limitations appear in RFC 2109,section 6.3,"Implementation Limits"。 有关详细信息,请参见"参考"一节。

每个 Cookie 开头名称-值对。 此对之后通过零或用分号分隔的多个属性-值对。 为一个域名称,每个 Cookie 被限制为 4,096 字节。 这个总数可以作为一个名称-值对的 4 千字节 (KB) 存在或为最多 20 个名称 / 值对的总 4 KB。如果计算机没有足够的空间来存储 Cookie,该 Cookie 将被丢弃。 则不截尾取整。 应用程序应使用尽可能少的 Cookie 可能作为小型和一个 Cookie 尽可能。 此外,应用程序应该能够处理 Cookie 的丢失。

如果 Web 应用程序使用多个 19 自定义 Cookie,ASP 会话状态可能会丢失。 Internet Explorer 4.0 和更高版本允许每个域的 20 个 Cookie 总共。 因为如果您使用 20 个或更多自定义 Cookie ASPSessionID 是一个 Cookie,在浏览器被强制删除 ASPSessionID Cookie 并丢失该会话。

若要存储超过 20 个名称 / 值对域的可以通过连接多达该 Cookie 4,096 字节的限制每个 Cookie 几个名称 / 值对创建 Cookie 字典。当前,以便从客户端脚本中检索这些值,您必须分析 Cookie 手动。 但是,Active Server Pages 请求 和 响应 对象包括内置功能使用 Cookie 词典作为词典对象。 following sample code demonstrates of in ASP page cookie dictionary use:


%
Response.Cookies ("MyCookie")("a")="A"
Response.Cookies ("MyCookie")("b")="B"
Response.Cookies ("MyCookie")("c")="C"
Response.Cookies ("MyCookie")("d")="D"
Response.Cookies ("MyCookie")("e")="E"
Response.Cookies ("MyCookie")("f")="F"
Response.Cookies ("MyCookie")("g")="G"
Response.Cookies ("MyCookie")("h")="H"
Response.Cookies ("MyCookie")("i")="I"
Response.Cookies ("MyCookie")("j")="J"
Response.Cookies ("MyCookie")("k")="K"
Response.Cookies ("MyCookie")("l")="L"
Response.Cookies ("MyCookie")("a1")="A"
Response.Cookies ("MyCookie")("b1")="B"
Response.Cookies ("MyCookie")("c1")="C"
Response.Cookies ("MyCookie")("d1")="D"
Response.Cookies ("MyCookie")("e1")="E"
Response.Cookies ("MyCookie")("f1")="F"
Response.Cookies ("MyCookie")("g1")="G"
Response.Cookies ("MyCookie")("h1")="H"
Response.Cookies ("MyCookie")("i1")="I"
Response.Cookies ("MyCookie")("j1")="J"
Response.Cookies ("MyCookie")("k1")="K"
Response.Cookies ("MyCookie")("l1")="L"

Response.Cookies("MyCookie").Expires = "12/31/2001"


For Each strKey In Request.Cookies
Response.Write strKey & " = " & Request.Cookies(strKey) & "

"
If Request.Cookies(strKey).HasKeys Then
For Each strSubKey In Request.Cookies(strKey)
Response.Write "->" & strKey & "(" & strSubKey & ") = " & _
Request.Cookies(strKey)(strSubKey) & "
"
Next
End If
Next
%


Note In Internet Explorer 5.0 and later,can use userData behavior to across sessions persist data. this behavior has greater than cookies capacity。

如果您使用 document.cookie 属性来检索该 Cookie 在客户端,将 document.cookie 属性可以检索仅 4,096 字节。 这个字节总数可以是 4 KB,一名称 / 值对或它可以是具有总大小为 4 KB 的最多 20 个名称 / 值对。

document.getcookie 函数在 Microsoft HTML 中调用 CDocument::GetCookie 方法。

for more information,click to view in Microsoft Knowledge Base article following article number:
820536 Document.Cookie 属性返回一个空字符串

官方看:http://support.microsoft.com/kb/306070/zh-cn

2008年12月8日星期一

PHP 从m个数中任取n个数的排列组合算法

从m个数中任取n个数的排列组合算法!

$n = array(1,2,3,4,5,6,7,8,9,10);
//$n = array(1,2,3,4);
/**
$n 数据数组
$m 组合串的长度
$s 用于缓存中间量
**/
function permutation($n, $m = 0, $s = ''){
static $results = array();

if($m == 0) {
$results[] = substr($s,1);//如果长度够了则赋值
}else {
for($i=0; $i < count($n); $i++) {
//echo "==$i===>".$s."\n";
permutation($n, $m-1, $s.','.$n[$i]);
}
}
return $results;
}

$t1 = microtime(true);
$rst = permutation($n, 2);
echo microtime(true) - $t1 ."\n";
//print_r($rst);

//////////////////
function combination($params){
foreach($params as $key => $val) {
$tmpArr = explode(',', $val);
if( 1 == count(array_unique($tmpArr))) {
continue;
}
sort($tmpArr);
$results[] = implode(',', $tmpArr);
}
$results = array_unique($results);

return $results;
}

$s = combination($rst);

print_r($s);
//exit;
function getCombinationCount($n, $k) {
//fscanf(STDIN,"%d,%d",$n,$k);
$r = 1;
while($k) $r *= $n--/$k--;
return $r;
}

echo count($s) . "===========" . getCombinationCount(10, 2);


exit;


加添一个快上N倍的算法:

var_dump(c(range(1,5), 2));

function c($ar,$n){
$c = count($ar);
if ($n>$c) return false; // parameter wrong
if ($c>50) return false; // too big array :)

$r = array();

$code = "";
$list = array();
for($i=1;$i<=$n;$i++){
$list[] = '$v'.$i;
$code .= 'foreach($ar as $k'.$i.'=>$v'.$i.'){';
if($i!=$n) $code .= 'unset($ar[$k'.$i.']);';
}
$code .= '$t = array('.join(',',$list).');';
$code .= 'sort($t);';
$code .= '$r[] = join(",",$t);';
for($i=$n-1;$i>0;$i--){
$code .= '}$ar[$k'.$i.']=$v'.$i.';';
}
$code .= '}';
/*
foreach($ar as $k1=>$v1){
unset($ar[$k1]);
foreach($ar as $k2=>$v2){
unset($ar[$k2]);
foreach($ar as $k3=>$v3){
$t = array($v1,$v2,$v3);
sort($t);
$r[] = join(',',$t);
}
$ar[$k2] = $v2;
}
$ar[$k1] = $v1;
}
*/
eval($code);
return array_values(array_unique($r));
}


再提供一下比快 N倍更快十倍的方法:
function array_combination(array $elements, $chosen)
{
$result = array();

for ($i = 0; $i < $chosen; $i++) { $vecm[$i] = $i; }
for ($i = 0; $i < $chosen-1; $i++) { $vecb[$i] = $i; }
$vecb[$chosen - 1] = count($elements) - 1;
$result[] = $vecm;

$mark = $chosen - 1;
while (true) {
if ($mark == 0) {
$vecm[0]++;
$result[] = $vecm;
if ($vecm[0] == $vecb[0]) {
for ($i = 1; $i < $chosen; $i++) {
if ($vecm[$i] < $vecb[$i]) {
$mark = $i;
break;
}
}
if (($i == $chosen) && ($vecm[$chosen - 1] == $vecb[$chosen - 1])) { break; }
}
} else {
$vecm[$mark]++;
$mark--;
for ($i = 0; $i <= $mark; $i++) {
$vecb[$i] = $vecm[$i] = $i;
}
$vecb[$mark] = $vecm[$mark + 1] - 1;
$result[] = $vecm;
}
}

return $result;
}


ini_set('memory_limit', '328M');
$n = 640 ; $m =2; $elements = range(1, $n);
$begin = microtime(TRUE);
$result = array_combination($elements, $m);
$end = microtime(TRUE);
echo "count: ".count($result) . PHP_EOL;
echo "time: ". 1000*($end-$begin) . " ms" . PHP_EOL;

echo '
';
//print_r($result);
echo '
';

/* output result
foreach($result as $indexes) {
foreach ($indexes as $index) {
echo $elements[$index] . "\t";
}

echo PHP_EOL;
}

*/