易栈网-膘叔

浏览模式: 标准 | 列表 | 图文
利用curl上传文件

利用curl上传文件

利用php的curl库上传文件真的很方便
注意几点
1、只能是POST
2、可以不写头(平时的form有文件和无文件时发送的时候,)
3、postfields的时候,不要你傻傻的http_build_query,你越是这么做了。黑黑。。。就反而不一定对了
4、上传的文件用@来连接。
一个很小的例子

$formAction = 'http://xxx/xxx/xx.upload.php';
$postVals = array(
// /var/www/www.jpg 这个地址是绝对地址哦。可以让程序读取
// windows下面可能就是d:/xxx/xxx/xxx.jpg
'img'=>'@/var/www/xxx.jpg', //img就相当于表单中的<input type="file" name="img" />

);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $formAction); //登录地址
curl_setopt($ch, CURLOPT_POST, 1); //这是POST数据
curl_setopt($ch, CURLOPT_POSTFIELDS, ($postVals)); //http_build_query( $postData)); ,如果你真用http_build_query,不一定能成功。所以。。。还是有curl自己去组合吧
$res = curl_exec($ch);
curl_close($ch);

看看多简单,一下子就成功了

车辆退税?

车辆退税?

今天中午突然有人自称是车管所工作人员(手机号码:15900789421),说是国家政策有变。可以购车退税了。。。然后说出我的购车车型,车价,以及我的车牌号。。(刚上牌2天)。
然后和我说,你可以联系国税局工作人员,它的手机是18817242664。
我一听很纳闷,怎么会这么巧?当然心里也是有点小激动的,哇退税啊。我可以补回来好几千了。。那我退多少呢?我得查查相关政策,结果一查,靠,网上全是退税骗局。(其实在他说国税局工作人员的号码是手机号的时候,我就在纠结了,怎么会是手机呢?为什么不是固定电话 ?)
于是我就电话 18817242664 ,果然他的流程与网上说的一模一样,(等会我会贴出全部流程,COPY来的),因为懒得去ATM机那边玩他们所以就说我可以通过网上银行。他们就说不行,这不可以。反正折腾了半天,于是我问他,你们单位在哪里,我可以去你们单位办理吗?他说只能网上处理,窗口不受理。我问,那你们在什么地方,他说是南京鼓楼区,我说,你不是靖江的吗?怎么是南京的了?他说,这个业务只是省国税局才能办,我说哦。那你为什么是手机呢?他说这是3G座机。咦,好象是有这个说法。
我再问,那你的号码为什么是上海的呢?他解释说,3G号码可以随便选地方的,就和IP地址一样的。我说哦,那我把你电话记下来了。他说好的,你这个人怎么这么麻烦 ,要是每个人都象你这样,我们国税局怎么工作?然后就挂机了。。。
-------------
新闻:去年购买的汽车可退税?骗局!http://www.hezeshi.com/news/200812/501/

  近日,市民王小姐接到了“省车管所工作人员”的通知:她去年4月购买的汽车有数千元退税。购买了大半年的汽车居然能够退税?“省车管所工作人员”会用手机打来电话通知?王小姐带着疑问致电本报。

  给王小姐打电话的手机号码是13055922923,电话中一位带着闽南口音的“浙江省车管所通知中心工作人员”说,王小姐去年4月购买的 1.8排量汽车有3%的退税,具体退税金额和退税方法可咨询“国家退税中心”,“国家退税中心”电话是010-51666586,“车辆退税编号”是 008177。

  记者打进“国家退税中心”电话,对方核对了车主姓名、退税编号后,准确地报出了王小姐的购车时间、车型,并称:“你的车可退税4285元。退款 已经以车主名义存入国库银行临时账号332862900236,密码为916918。你可通过财政部国库语音查询系统4006269747进行转账。”

  为什么去年购买的汽车也可退税?对方表示,这是“国家税务”让利消费者。1.6升及以下的汽车退税幅度为5%,其他排量的汽车退税幅度较小,仅为3%。王小姐是电脑选号“选出”的第一批车主。

  记者随后拨通了“财政部语音查询系统”,对方先让车主查询账面金额,再提示车主选择相应的银行,输入账号和密码“转账”。至此,骗局终于走到了关键一步。

  有王小姐经历的车主还真不少,就在当天,记者身边就有3位朋友接到这样的电话,来电手机号是福建厦门、广东惠州等地的。

  记者致电12366税收服务热线。工作人员表示,购车退税的情况是有的,但非常有限:一是因质量原因,车辆被退回生产企业或经销商;二是应当办 理车辆登记注册的车辆,公安机关车辆管理机构不予办理车辆登记注册。若真的有退税新政策出台,税务部门会在媒体公告,符合条件的纳税人在指定时间到税务局 办理,而非通过电话逐个通知。

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

  新闻链接

  电话短信诈骗招招都可识破

  近期,短信、电话诈骗现象有上升之势,公安部门提醒市民,对以下几种诈骗方式要提高警惕:

  一、接到陌生电话,对方自称是你好友,曾有一定交往,并让你猜其身份。请不要跟他捉迷藏,直接问他是谁,简单核对就清楚了。

  二、对方在电话中打着上级领导身边工作人员的旗号要求你帮忙处理领导个人事务。此时你要多长个心眼,特别是不要急于付钱,待核实他身份后再帮忙也不迟。

  三、如果你接到“我是××省公证处的公证员××,恭喜你的手机或电话号码在××抽奖中中了×等奖,奖品是小轿车一辆……”接到这样的短信,请先回想一下,想想自己是否参加过将电话号码投入摇奖机等什么活动,如果没有请不要回复。

  四、“您好,您的朋友为您点播了一首××歌曲,以此表达他的思念和祝福,请您拨打×××收听。”当你拨打电话收听时,话费余额就会直线下降。

  五、如果你接到未知电话或响过一两下就挂断了,请不要随便回复,部分号码有的是加值付费电话,接到这类电话或陌生来电,一定要小心核对再行回复。

  六、“您好,这里是中国移动(或联通)客户服务热线,由于我们工作失误,您的电话费这几个月共多收×××元,如确认退费请按……”接到类似电话后只要认真查看来电号码,就可识破骗局。

  七、以××银行名义提醒:你在某地(一般在本市大型商场、超市或娱乐场所)刷卡消费,金额将于近期从你账户中扣除。如果你收到类似手机短信,再按短信里的电话询问,就可能栽进不法之徒的圈套。

  八、“本公司与电信制卡部门联合推出移动、联通手机卡充值,100元面值的现以××元低价促销,诚招各地代理经销商。”如果收到这种信息,请不要被低价诱惑,最好向移动与联通客户服务中心咨询是否属实。

-----------
更多网上的信息:退税中心

推荐小程序:TimThumb – PHP Image Resizer

推荐小程序:TimThumb – PHP Image Resizer

这是一个单文件处理缩略图的程序,直接引用生成缩略图。有点意思,主要是方便啦 .。

官方地址是:http://www.binarymoon.co.uk/projects/timthumb/
文件也很小,只有一点点大,更关键的是可以缓存回来。也能取远程的图片,也可以设置allow的安全站点,这样就相对安全一点了,官方这么介绍 :
TimThumb is a simple, flexible, PHP script that resizes images. You give it a bunch of parameters, and it spits out a thumbnail image that you can display on your site.

TimThumb has seen a massive amount of use across the WordPress world, and a few months after we released it, I took over development from Tim, a friend of Darren Hoyts, who created the original script. Since I took over there have been a whole host of changes, bug fixes, and additions.

I set up this page to act as an archive/ resource, showing the best documentation and demos available for TimThumb. If you have any TimThumb related articles then please feel free to send them over to me so that I can add them to the list.

功能也相对比较方便,用法也很简单,官方现成就举了一些例子,并通过这些例子介绍了它的参数:

How to use TimThumb

看了上面的使用方法,再看看,这个,你就知道很多常见的方法和用法了:

stands for values What it does
src source url to image Tells TimThumb which image to resize › tutorial
w width the width to resize to Remove the width to scale proportionally (will then need the height) › tutorial
h height the height to resize to Remove the height to scale proportionally (will then need the width) › tutorial
q quality 0 - 100 Compression quality. The higher the number the nicer the image will look. I wouldn't recommend going any higher than about 95 else the image will get too large › tutorial
a alignment c, t, l, r, b, tl, tr, bl, br Crop alignment. c = center, t = top, b = bottom, r = right, l = left. The positions can be joined to create diagonal positions › tutorial
zc zoom / crop 0, 1, 2, 3 Change the cropping and scaling settings › tutorial
f filters too many to mention Let's you apply image filters to change the resized picture. For instance you can change brightness/ contrast or even blur the image › tutorial
s sharpen Apply a sharpen filter to the image, makes scaled down images look a little crisper › tutorial
cc canvas colour hexadecimal colour value (#ffffff) Change background colour. Most used when changing the zoom and crop settings, which in turn can add borders to the image.
ct canvas transparency true (1) Use transparency and ignore background colour

参数很简单,就是不太好记。。。纠结啊

不过用起来的话,是真心简单

旧文:QeePHP中的优秀函数(三)

旧文:QeePHP中的优秀函数(三)

这几个函数还是来自于QeePHP的核心类Q中。不过,我是自认为,我的configure类有部分写的比他好,不过我没有考虑删除之类的。呵呵。

/**
* 获取指定的设置内容
*
* $option 参数指定要获取的设置名。
* 如果设置中找不到指定的选项,则返回由 $default 参数指定的值。
*
* @code php
* $option_value = Q::ini('my_option');
* @endcode
*
* 对于层次化的设置信息,可以通过在 $option 中使用“/”符号来指定。
*
* 例如有一个名为 option_group 的设置项,其中包含三个子项目。
* 现在要查询其中的 my_option 设置项的内容。
*
* @code php
* // +--- option_group
* // +-- my_option = this is my_option
* // +-- my_option2 = this is my_option2
* // \-- my_option3 = this is my_option3
*
* // 查询 option_group 设置组里面的 my_option 项
* // 将会显示 this is my_option
* echo Q::ini('option_group/my_option');
* @endcode
*
* 要读取更深层次的设置项,可以使用更多的“/”符号,但太多层次会导致读取速度变慢。
*
* 如果要获得所有设置项的内容,将 $option 参数指定为 '/' 即可:
*
* @code php
* // 获取所有设置项的内容
* $all = Q::ini('/');
* @endcode
*
* @param string $option 要获取设置项的名称
* @param mixed $default 当设置不存在时要返回的设置默认值
*
* @return mixed 返回设置项的值
*/
static function ini($option, $default = null)
{
if ($option == '/') return self::$_ini;

if (strpos($option, '/') === false)
{
return array_key_exists($option, self::$_ini)
? self::$_ini[$option]
: $default;
}

$parts = explode('/', $option);
$pos =& self::$_ini;
foreach ($parts as $part)
{
if (!isset($pos[$part])) return $default;
$pos =& $pos[$part];
}
return $pos;
}

/**
* 修改指定设置的内容
*
* 当 $option 参数是字符串时,$option 指定了要修改的设置项。
* $data 则是要为该设置项指定的新数据。
*
* @code php
* // 修改一个设置项
* Q::changeIni('option_group/my_option2', 'new value');
* @endcode
*
* 如果 $option 是一个数组,则假定要修改多个设置项。
* 那么 $option 则是一个由设置项名称和设置值组成的名值对,或者是一个嵌套数组。
*
* @code php
* // 假设已有的设置为
* // +--- option_1 = old value
* // +--- option_group
* // +-- option1 = old value
* // +-- option2 = old value
* // \-- option3 = old value
*
* // 修改多个设置项
* $arr = array(
* 'option_1' => 'value 1',
* 'option_2' => 'value 2',
* 'option_group/option2' => 'new value',
* );
* Q::changeIni($arr);
*
* // 修改后
* // +--- option_1 = value 1
* // +--- option_2 = value 2
* // +--- option_group
* // +-- option1 = old value
* // +-- option2 = new value
* // \-- option3 = old value
* @endcode
*
* 上述代码展示了 Q::changeIni() 的一个重要特性:保持已有设置的层次结构。
*
* 因此如果要完全替换某个设置项和其子项目,应该使用 Q::replaceIni() 方法。
*
* @param string|array $option 要修改的设置项名称,或包含多个设置项目的数组
* @param mixed $data 指定设置项的新值
*/
static function changeIni($option, $data = null)
{
if (is_array($option))
{
foreach ($option as $key => $value)
{
self::changeIni($key, $value);
}
return;
}

if (!is_array($data))
{
if (strpos($option, '/') === false)
{
self::$_ini[$option] = $data;
return;
}

$parts = explode('/', $option);
$max = count($parts) - 1;
$pos =& self::$_ini;
for ($i = 0; $i < = $max; $i ++)
{
$part = $parts[$i];
if ($i < $max)
{
if (!isset($pos[$part]))
{
$pos[$part] = array();
}
$pos =& $pos[$part];
}
else
{
$pos[$part] = $data;
}
}
}
else
{
foreach ($data as $key => $value)
{
self::changeIni($option . '/' . $key, $value);
}
}
}

/**
* 替换已有的设置值
*
* Q::replaceIni() 表面上看和 Q::changeIni() 类似。
* 但是 Q::replaceIni() 不会保持已有设置的层次结构,
* 而是直接替换到指定的设置项及其子项目。
*
* @code php
* // 假设已有的设置为
* // +--- option_1 = old value
* // +--- option_group
* // +-- option1 = old value
* // +-- option2 = old value
* // \-- option3 = old value
*
* // 替换多个设置项
* $arr = array(
* 'option_1' => 'value 1',
* 'option_2' => 'value 2',
* 'option_group/option2' => 'new value',
* );
* Q::replaceIni($arr);
*
* // 修改后
* // +--- option_1 = value 1
* // +--- option_2 = value 2
* // +--- option_group
* // +-- option2 = new value
* @endcode
*
* 从上述代码的执行结果可以看出 Q::replaceIni() 和 Q::changeIni() 的重要区别。
*
* 不过由于 Q::replaceIni() 速度比 Q::changeIni() 快很多,
* 因此应该尽量使用 Q::replaceIni() 来代替 Q::changeIni()。
*
* @param string|array $option 要修改的设置项名称,或包含多个设置项目的数组
* @param mixed $data 指定设置项的新值
*/
static function replaceIni($option, $data = null)
{
if (is_array($option))
{
self::$_ini = array_merge(self::$_ini, $option);
}
else
{
self::$_ini[$option] = $data;
}
}

/**
* 删除指定的设置
*
* Q::cleanIni() 可以删除指定的设置项目及其子项目。
*
* @param mixed $option 要删除的设置项名称
*/
static function cleanIni($option)
{
if (strpos($option, '/') === false)
{
unset(self::$_ini[$option]);
}
else
{
$parts = explode('/', $option);
$max = count($parts) - 1;
$pos =& self::$_ini;
for ($i = 0; $i < = $max; $i ++)
{
$part = $parts[$i];
if ($i < $max)
{
if (!isset($pos[$part]))
{
$pos[$part] = array();
}
$pos =& $pos[$part];
}
else
{
unset($pos[$part]);
}
}
}
}
旧文:QeePHP中的优秀函数(二)

旧文:QeePHP中的优秀函数(二)

这两个函数来自于Helper_Array,我觉得是非常常用的方法,功能也比较强大。适合大家使用。

/**
* 将一个平面的二维数组按照指定的字段转换为树状结构
*
* 用法:
* @code php
* $rows = array(
* array('id' => 1, 'value' => '1-1', 'parent' => 0),
* array('id' => 2, 'value' => '2-1', 'parent' => 0),
* array('id' => 3, 'value' => '3-1', 'parent' => 0),
*
* array('id' => 7, 'value' => '2-1-1', 'parent' => 2),
* array('id' => 8, 'value' => '2-1-2', 'parent' => 2),
* array('id' => 9, 'value' => '3-1-1', 'parent' => 3),
* array('id' => 10, 'value' => '3-1-1-1', 'parent' => 9),
* );
*
* $tree = Helper_Array::tree($rows, 'id', 'parent', 'nodes');
*
* dump($tree);
* // 输出结果为:
* // array(
* // array('id' => 1, ..., 'nodes' => array()),
* // array('id' => 2, ..., 'nodes' => array(
* // array(..., 'parent' => 2, 'nodes' => array()),
* // array(..., 'parent' => 2, 'nodes' => array()),
* // ),
* // array('id' => 3, ..., 'nodes' => array(
* // array('id' => 9, ..., 'parent' => 3, 'nodes' => array(
* // array(..., , 'parent' => 9, 'nodes' => array(),
* // ),
* // ),
* // )
* @endcode
*
* 如果要获得任意节点为根的子树,可以使用 $refs 参数:
* @code php
* $refs = null;
* $tree = Helper_Array::tree($rows, 'id', 'parent', 'nodes', $refs);
*
* // 输出 id 为 3 的节点及其所有子节点
* $id = 3;
* dump($refs[$id]);
* @endcode
*
* @param array $arr 数据源
* @param string $key_node_id 节点ID字段名
* @param string $key_parent_id 节点父ID字段名
* @param string $key_childrens 保存子节点的字段名
* @param boolean $refs 是否在返回结果中包含节点引用
*
* return array 树形结构的数组
*/
static function toTree($arr, $key_node_id, $key_parent_id = 'parent_id',
$key_childrens = 'childrens', & $refs = null)
{
$refs = array();
foreach ($arr as $offset => $row)
{
$arr[$offset][$key_childrens] = array();
$refs[$row[$key_node_id]] =& $arr[$offset];
}

$tree = array();
foreach ($arr as $offset => $row)
{
$parent_id = $row[$key_parent_id];
if ($parent_id)
{
if (!isset($refs[$parent_id]))
{
$tree[] =& $arr[$offset];
continue;
}
$parent =& $refs[$parent_id];
$parent[$key_childrens][] =& $arr[$offset];
}
else
{
$tree[] =& $arr[$offset];
}
}

return $tree;
}

/**
* 将树形数组展开为平面的数组
*
* 这个方法是 tree() 方法的逆向操作。
*
* @param array $tree 树形数组
* @param string $key_childrens 包含子节点的键名
*
* @return array 展开后的数组
*/
static function treeToArray($tree, $key_childrens = 'childrens')
{
$ret = array();
if (isset($tree[$key_childrens]) && is_array($tree[$key_childrens]))
{
$childrens = $tree[$key_childrens];
unset($tree[$key_childrens]);
$ret[] = $tree;
foreach ($childrens as $node)
{
$ret = array_merge($ret, self::treeToArray($node, $key_childrens));
}
}
else
{
unset($tree[$key_childrens]);
$ret[] = $tree;
}
return $ret;
}
不过显而易见,这两个函数,都不需要多介绍,tree2list,list2tree,想想也知道怎么用,再加上注释又比较全。
可惜QeePHP不再开发,而ThinkPHP积下来的问题又很多,改动起来也非常痛苦。所以我开始慢慢分析一下。
旧文:QeePHP中的优秀函数(一)

旧文:QeePHP中的优秀函数(一)

基类Q中的normalize。

/**
* 对字符串或数组进行格式化,返回格式化后的数组
*
* $input 参数如果是字符串,则首先以“,”为分隔符,将字符串转换为一个数组。
* 接下来对数组中每一个项目使用 trim() 方法去掉首尾的空白字符。最后过滤掉空字符串项目。
*
* 该方法的主要用途是将诸如:“item1, item2, item3” 这样的字符串转换为数组。
*
* @code php
* $input = 'item1, item2, item3';
* $output = Q::normalize($input);
* // $output 现在是一个数组,结果如下:
* // $output = array(
* // 'item1',
* // 'item2',
* // 'item3',
* // );
*
* $input = 'item1|item2|item3';
* // 指定使用什么字符作为分割符
* $output = Q::normalize($input, '|');
* @endcode
*
* @param array|string $input 要格式化的字符串或数组
* @param string $delimiter 按照什么字符进行分割
*
* @return array 格式化结果
*/
static function normalize($input, $delimiter = ',')
{
if (!is_array($input))
{
$input = explode($delimiter, $input);
}
$input = array_map('trim', $input);
return array_filter($input, 'strlen');
}
看到这个方法,其实应该感觉得到它的用户,确实,大多数情况下,我们都是用explode来分割字符串的,但事实上,你不能保证别人给你的字符串有没有多 余的空格,或者无效的字符串,因此,通过这个函数,就可以去除掉很无效的值 。建议使用它来代替explode,但,如果确实是很规则的,还是用explode吧,毕竟,如果这个分割出来的数组很大,效率肯定比explode低很 多了。
【老博客】Using Bloom Filters

【老博客】Using Bloom Filters

这,又是两年前的博客了。但觉得还是一个不错的东西,所以还是贴出来吧。。

前段时间,yhustc问群里的人说是,如果有两个4G的文件,怎么样把其中相同的URL取出来?(文件大小4G,每行一个URL,每个URL64个字节),一下子迷惘了。后来他说了这个Bloom Filters,于是找了点资料 。
以下为部分资料,下次贴带图片(公式)的。。【文中有图片,但事实上原文并没有图片,来源于http://www.chinaunix.net/jh/25/601028.html】
仙子注:这篇文章是半年前翻译的,最早贴于公司内部的BBS上,并引起一些争论。Bloom Filters是一种效率较高的内存索引算法,它本身具有矛盾性:一方面能快速测试目标成员是否存在,另一方面又不可避免的具有假命中率。如下文档仅供参考。
由于不知道如何在这里粘贴图片,因此本文中没有包含图片说明,请对照原文档来阅读,原文档在:http://www.perl.com/pub/a/2004/04/08/bloom_filters.html?page=1 或可email给我索取中文PDF文档。

使用Bloom Filters

原作者:Maciej Ceglowski
April 08, 2004

任何perl使用者都熟悉hash查询,一个存在测试的语句可以这样写:

foreach my $e ( @things ) { $lookup{$e}++ }  sub check { my ( $key ) = @_; print "Found $key!" if exists( $lookup{ $key } ); }

虽然hash查询很有用,但对非常大的列表,或keys自身非常大时,这种查询可能变得不实用。当查询hash增长得太大,通常的做法是将它移到数据库或文件中,只在本地缓存里保存最常用的关键字,这样能改善性能。

许多人不知道有一种优雅的算法,用以代替hash查询。它是一种古老的算法,叫做Bloom filter。 Bloom filter允许你在有限的内存里(你想在这块内存里存放关键字的完整列表),执行成员测试,这样就能避开使用磁盘或数据库进行查询的性能瓶颈。也许你会 认为,空间的节省是有代价的:存在着可大可小的假命中率风险,并且一旦你增加key到filter后,就不能删除它。然而在许多情形下,这些局限是可接受 的,bloom filter能编制有用工具。(仙子注:例如代理服务器软件Squid就使用了bloom filter算法。)

例如,假如你运行了一个高流量的在线音乐存储站点,并且如果你已知歌曲存在,就可以通过仅获取歌曲信息的方法,来最大程度的减少数据库压力。你可以在启动时构建一个bloom filter,在试图执行昂贵的数据库查询前,可以用它执行快速的成员存在测试。

use Bloom::Filter;  my $filter = Bloom::Filter->new( error_rate => 0.01, capacity => $SONG_COUNT ); open my $fh, "enormous_list_of_titles.txt" or die "Failed to open: $!";  while (< $fh>) { chomp; $filter->add( $_ ); }  sub lookup_song { my ( $title ) = @_; return unless $filter->check( $title ); return expensive_db_query( $title ) or undef; }

在该示例里,该测试给出假命中的几率是1%,在假命中率情况下程序会执行昂贵的数据库索取操作,并最终返回空结果。尽管如此,你已避开了99%的昂 贵查询时间,仅使用了用于hash查询的一小片内存。更进一步,1%假命中率的filter,每个key的存储空间在2字节以下。这比你执行完整的 hash查询所需的内存少得多。

bloom filters在Burton Bloom之后命名,Burton Bloom 1970年首先在文档里描述了它们,文档名 Space/time trade-offs in hash coding with allowable errors.在那些内存稀少的日子里,bloom filters因其简洁而倍受重视。事实上,最早的应用之一是拼写检查程序。然而,由于有少数非常明显的特性,该算法特别适合社会软件应用。

因为bloom filters使用单向hash来存储数据,因此不可能在不做穷举搜索的情况下,重建filter里的keys列表。甚至这点看起来并非象很有用,既然来 自穷举搜索的假命中会覆盖掉真正的keys列表。所以bloom filters能在不向全世界广播完整列表的情况下,共享关于已有资料的信息。因为这个理由,它们在peer-to-peer应用中特别有用,在这个应用 中大小和隐私是重要的约束。

bloom filters如何工作

bloom filter由2部分组成:1套k hash函数,1个给定长度的位向量。选择位向量的长度,和hash函数的数量,依赖于我们想增加多少keys到设置中,以及我们能容忍的多高的假命中率。

bloom filter中所有的hash函数被配置过,其范围匹配位向量的长度。例如,假如向量是200位长,hash函数返回的值就在1到 200之间。在filter里使用高质量的hash函数相当重要,它保证输出等分在所有可能值上--hash函数里的“热点”会增加假命中率。(仙子注: 所谓“热点”是指结果过分频繁的分布在某些值上。)

要将某个key输入bloom filer中,我们在每个k hash函数里遍历它,并将结果作为在位向量里的offsets,并打开我们在该offsets上找到的任何位。假如该位已经设置,我们继续保留其打开。还没有在bloom filter里关闭位的机制。

在本示例里,让我们看看某个bloom filter,它有3个hash函数,并且位向量的长度是14。我们用空格和星号来表示位向量,以便于观察。你也许想到,空的bloom filter以所有的位关闭为开始,如图1所示。

图1:空的bloom filter

现在我们将字符apples增加到filter中去。为了做到这点,我们以apples为参数来运行每个hash函数,并采集输出:

hash1(“apples”) = 3
hash2(“apples”) = 12
hash3(“apples”) = 11

然后我们打开在向量里相应位置的位--在这里就是位3,11,和12,如图2所示。

图2:激活了3位的bloom filter

为了增加另1个key,例如plums,我们重复hash运算过程:

hash1(“plums”) = 11
hash2(“plums”) = 1
hash3(“plums”) = 8

再次打开向量里相应的位,如图3里的高亮度显示。

图3:增加了第2个key的bloom filter

注意位置11的位已被打开--在前面的步骤里,当我们增加apples时已设置了它。位11现在有双重义务,存储apples和plums两者的信 息。当增加更多的keys时,它也会存储其他keys的信息。这种交迭让bloom filters如此紧凑--任何位同时编码多个keys。这种交迭也意味着你永不能从filter里取出key,因为你不能保证你所关闭的位没有携载其他 keys的信息。假如我们试图执行反运算过程来从filter里删除apples,就会不经意的关闭编码plums的1个位。从bloom filter里剥离key的唯一方法是重建filter,剔除无用key。

检查是否某个key已经存在于filter的过程,非常类似于增加新key。我们在所有的hash函数里遍历key,然后检查是否在那些 offsets上的位都是打开的。假如任何一位关闭,我们知道该key肯定不存在于filter中。假如所有位都打开,我们知道该key可能存在。

我说“可能”是因为存在一种情况,该key是个假命中。例如,假如我们用字符mango来测试filter,看看会发生什么情况。我们运行mango遍历hash函数:

hash1(“mango”) = 8
hash2(“mango”) = 3
hash3(“mango”) = 12

然后检查在那些offsets上的位,如图4所示。

图4:bloom filter的假命中

所有在位置3,8,和12的位都是打开的,故filter会报告mango是有效key。

当然,mango并非有效key--我们构建的filter仅包含apples和plums。事实是mango的offsets非常巧合的指向了已激活的位。这就找到了1个假命中--某个key看起来位于filter中,但实际不是。

正如你想的一样,假命中率依赖于位向量的长度和存储在filter里的keys的数量。位向量越宽阔,我们检查的所有k位被打开的可能性越小,除非 该key确实存在于filter中。在hash函数的数量和假命中率之间的关系更敏感。假如使用的hash函数太少,在keys之间的差别就很少;但假如 使用hash函数太多,filter会过于密集,增加了冲突的可能性。可以使用如下公式来计算任何filter的假命中率:

c = ( 1 – e(-kn/m) )k

这里c是假命中率,k是hash函数的数量,n是filter里keys的数量,m是filter的位长。

当使用bloom filters时,我们先要有个意识,期待假命中率多大;也应该有个粗糙的想法,关于多少keys要增加到filter里。我们需要一些方法来验证需要多大的位向量,以保证假命中率不会超出我们的限制。下列方程式会从错误率和keys数量求出向量长度:

m = -kn / ( ln( 1 – c ^ 1/k ) )

请注意另1个自由变量:k,hash函数的数量。可以用微积分来得出k的最小值,但有个偷懒的方法来做它:

sub calculate_shortest_filter_length { my ( $num_keys, $error_rate ) = @_; my $lowest_m; my $best_k = 1;  foreach my $k ( 1..100 ) { my $m = (-1 * $k * $num_keys) /  ( log( 1 - ($error_rate ** (1/$k))));  if ( !defined $lowest_m or ($m < $lowest_m) ) { $lowest_m = $m; $best_k   = $k; } } return ( $lowest_m, $best_k ); }

为了给你直观的感觉,关于错误率和keys数量如何影响bloom filters的存储size,表1列出了一些在不同的容量/错误率组合下的向量size。

ErrorRate Keys RequiredSize Bytes/Key
1% 1K 1.87 K 1.9
0.1% 1K 2.80 K 2.9
0.01% 1K 3.74 K 3.7
0.01% 10K 37.4 K 3.7
0.01% 100K 374 K 3.7
0.01% 1M 3.74 M 3.7
0.001% 1M 4.68 M 4.7
0.0001% 1M 5.61 M 5.7

在Perl里构建bloom filter

为了构建1个工作bloom filter,我们需要1套良好的hash函数。这些容易解决--在CPAN上有几个优秀的hash算法可用。对我们的目的来说,较好的选择是 Digest::SHA1,它是强度加密的hash,用C实现速度很快。通过对不同值的输出列表进行排序,我们能使用该模块来创建任意数量的hash函 数。如下是构建唯一hash函数列表的子函数:

use Digest::SHA1 qw/sha1/;  sub make_hashing_functions { my ( $count ) = @_; my @functions;  for my $salt (1..$count ) { push @functions, sub { sha1( $salt, $_[0] ) }; }  return @functions; }

为了能够使用这些hash函数,我们必须找到1个方法来控制其范围。Digest::SHA1返回令人为难的过长160位hash输出,这仅在向量 长度为2的160次方时有用,而这种情况实在罕见。我们结合使用位chopping和division来将输出削减到可用大小。

如下子函数取某个key,运行它遍历hash函数列表,并返回1个长度($FILTER_LENGTH)的位掩码:

sub make_bitmask { my ( $key ) = @_; my $mask    = pack( "b*", '0' x $FILTER_LENGTH);  foreach my $hash_function ( @functions ){   my $hash       = $hash_function->($key); my $chopped    = unpack("N", $hash ); my $bit_offset = $result % $FILTER_LENGTH;  vec( $mask, $bit_offset, 1 ) = 1;        } return $mask; }

让我们逐行分析上述代码:

my $mask = pack( "b*", '0' x $FILTER_LENGTH);

我们以使用perl的pack操作来创建零位向量开始,它是$FILTER_LENGTH长。pack取2个参数,1个模型和1个值。b模型告诉 pack将值解释为bits,*指“重复任意多需要的次数”,跟正则表达式类似。perl实际上会补充位向量的长度为8的倍数,但我们将忽视这些多余位。

有1个空的位向量在手中,我们准备开始运行key遍历hash函数:

my $hash = $hash_function->($key); my $chopped = unpack("N", $hash );

我们保存首个32位输出,并丢弃剩下的。这点可让我们不必要求BigInt支持。第2行做实际的位chopping。模型里的N告诉unpack以网络字节顺序来解包32位整数。因为未在模型里提供任何量词,unpack仅解包1个整数,然后终止。

假如你对位chopping过度狂热,你可以将hash分割成5个32位的片断,并对它们一起执行OR运算,将所有信息保存在原始hash里:

my $chopped = pack( "N", 0 ); my @pieces  =  map { pack( "N", $_ ) } unpack("N*", $hash ); $chopped    = $_ ^ $chopped foreach @pieces;

但这样作可能杀伤力过度。

现在我们有了来自hash函数的32位整数输出的列表,下一步必须做的是,裁减它们的大小,以使其位于(1..$FILTER_LENGTH)范围内。

my $bit_offset = $chopped % $FILTER_LENGTH;

现在我们已转换key为位offsets列表,这正是我们所求的。

剩下唯一要做的事情是,使用vec来设置位,vec取3个参数:向量自身,开始位置,要设置的位数量。我们能象赋值给变量一样来分配值给vec:

vec( $mask, $bit_offset, 1 ) = 1;

在设置了所有位后,我们以1个位掩码来结束,位掩码和bloom filter长度一样。我们可以使用这个掩码来增加key到filter中:

sub add { my ( $key, $filter ) = @_;  my $mask = make_bitmask( $key ); $filter  = $filter | $mask; }

或者我们使用它来检查是否key已存在:

sub check { my ( $key, $filter ) = @_; my $mask  = make_bitmask( $key ); my $found = ( ( $filter & $mask ) eq $mask ); return $found; }

注意这些是位逻辑运算符OR(|)和AND(&),而并非通用的逻辑OR(||)和AND(&&)运算符。将这两者混在一 起,会导致数小时的有趣调试。第1个示例将掩码和位向量进行OR运算,打开任何未设置的位。第2个示例将掩码和filter里相应的位置进行比较--假如 掩码里所有的打开位也在filter里打开,我们知道已找到一个匹配。

一旦你克服了使用vec,pack和位逻辑运算符的难度,bloom filters实际非常简单。http://www.perl.com/2004/04/08/examples/Filter.pm 这里给出了Bloom::Filter模块的完整信息。

分布式社会网络中的bloom filters

当前的社会网络机制的弊端之一是,它们要求参与者泄露其联系列表给中央服务器,或公布它到公共Internet,这2种情况下都牺牲了大量的用户隐 私。通过交换bloom filters而不是暴露联系列表,用户能参与社会网络实践,而不用通知全世界他们的朋友是谁。编码了某人联系信息的 bloom filter能用来检查它是否包含了给定的用户名或email地址,但不能强迫要求它展示用于构建它的完整keys列表。甚至有可能将假命中率(虽然它听 起来不像好特性),转换为有用工具。

假如我非常关注这些人,他们通过对bloom filter运行字典攻击,来试图对社会网络进行反工程。我可以构建filter,它具备较高的假命中率(例如50%),然后发送filter的多个拷贝 给朋友,并变换用于构建每个filter的hash函数。我的朋友收集到的filters越多,他们见到的假命中率越低。例如,在5个filters情况 下,假命中率是0.5的5次方,或3%--通过发送更多filters,还能进一步减少假命中率。

假如这些filters中的任何一个被中途截取,它会展示全部50%的假命中率。所以我能隔离隐私风险,并且一定程度上能控制其他人能多清楚的

了解我的网络。我的朋友能较高程度的确认是否某个人位于联系列表里,但那些仅截取了1个或2个filters的人,几乎不会获取到什么。如下是个perl函数,它对1组嘈杂的filters检查某个key:

use Bloom::Filter;          sub check_noisy_filters { my ( $key, @filters ) = @_; foreach my $filter ( @filters ) { return 0 unless $filter->check( $key ); } return 1; }

假如你和你的朋友同意使用相同的filter长度和hash函数设置,你也能使用位掩码对比来估计在你们的社会网络之间的交迭程度。在2个bloom filters里的共享位数量会给出1个可用的距离度量。

sub shared_on_bits { my ( $filter_1, $filter_2 ) = @_; return unpack( "%32b*",  $filter_1 & $filter_2 ) }

另外,你能使用OR运算,结合2个有相同长度和hash函数的bloom filters来创建1个复合filter。例如,假如你参与某个小型邮件列表,并希望基于组里每个人的地址本来创建白名单,你可以为每个参与者独立的创 建1个bloom filter,然后将filters一起进行OR运算,将结果输入Voltron-like主列表。组里成员不会了解到其他成员的联系信息,并且 filter仍能展示正确的行为。

肯定还有其他针对社会网络和分布式应用的bloom filter妙用。如下参考列出一些有用资源。

参考
· Bloom Filters — the math. A good place to start for an overview of the math behind Bloom filters.
· Some Motley Bloom Tricks. Handy filter tricks and theory page.
· Bloom Filter Survey. A handy survey article on Bloom filter network applications.
· LOAF. Our own system for incorporating social networks onto email using Bloom filters.
· Compressed Bloom Filters. If you are passing filters around a network, you will want to optimize them for minimum size; this paper gives a good overview of compressed Bloom filters.
· Bloom16. A CPAN module implementing a counting Bloom filter.
· Text::Bloom. CPAN module for using Bloom filters with text collections.
· Privacy-Enhanced Searches Using Encryted Bloom Filters. This paper discusses how to use encryption and Bloom filters to set up a query system that prevents the search engine from knowing the query you are running.
· Bloom Filters as Summaries. Some performance data on actually using Bloom filters as cache summaries.
· Using Bloom Filters for Authenticated Yes/No Answers in the DNS. Internet draft for using Bloom filters to implement Secure DNS

采用CURL登录量子统计

采用CURL登录量子统计

这是我很久前的文字了。只是。。。那个博客其实已经废掉了,被我用来尝试着赚点零花钱(域名费而已)
所以,这两天在用到它的时候,又COPY出来,做个备份。虽然其中大部分代码都有点一样,但其实还是略有区别,我这里还是贴我以前的文章。现在项目中用的就不贴了。。。

我自己的站点用的是量子统计,所以对于量子统计就需要有更多的研究和学习。
我想的是,如果我能够通过手机,每天在自己的某个指定页面,就能够看到朋友们过来时的关键字或者搜索引擎关键字等。我就可以利用他们有针对性的对网站进行优化?也可以随时关注网站的流量等信息了。

于是,想到什么就做什么,我直接用snoopy类开始尝试提交post数据,提交后取后返回值 ,并同时打开http://tongji.linezing.com/mystat.html页面(这是一个汇总页),但结果都是提示我需要登录。

如此反复尝试多次,也尝试用curl进行登录,但都是一直失败。最后我不得不祭起抓包利器:smartsniff,这是一个小工具,但是他的抓包功 能很强。于是我对我的行为开始抓包,从登录直到显示mystat.html页面,结果却发现,从登录开始,到显示mystat.html页面,一共抓了四 次包,他们分别是:

  1. http://www.linezing.com/login.php 登录提交页,POST提交
  2. http://www.linezing.com/router.php GET方式
  3. http://tongji.linezing.com/welcome.html GET方式
  4. http://tongji.linezing.com/mystat.html GET方式

其实我觉得奇怪的是,这四个页面是在同一台机器上,而且主机名却是bbs.linezing.com,好妖异。

不过,既然分析了抓包数据,得到这四个页面,那么剩下的就开始写代码了,欲知后事如何,请看周一的代码分析(其实是因为代码在单位的电脑上,在家里地无法更新而己),敬请关注。

$cookiefile = tempnam( './log/' , 'cookie' );//设定cookie文件的路径 。
$ch = curl_init();
$header[]="Content-Type: application/x-www-form-urlencoded";
curl_setopt($ch, CURLOPT_URL, 'http://www.linezing.com/login.php'); //登录地址
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //发送header ,其实这个header可以不发送
curl_setopt($ch, CURLOPT_POST, 1); //这是POST数据
curl_setopt($ch, CURLOPT_POSTFIELDS, 'referer=&webname=index&username=用户名&password=密码&submit=%E7%99%BB%E5%BD%95');//http_build_query( $postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //这个是代表curl_exec后取返回成字符串,而不是象WEB一样跳转
curl_setopt($ch, CURLOPT_HEADER, 0); //curl返回的时候,默认都是带有header信息的,所以这里设为0,代表返回的时候不要header信息
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); //这是在用sniff抓包的时候发现用了gzip,deflate的encoding,
curl_setopt($ch, CURLOPT_REFERER, 'http://www.linezing.com/');//记录来源的Referer
curl_setopt($ch, CURLOPT_COOKIEFILE,$cookiefile);
curl_setopt($ch, CURLOPT_COOKIEJAR,$cookiefile);
curl_exec($ch); //我这里并没有取返回值,主要是把cookie记录下来

curl_setopt($ch, CURLOPT_URL, 'http://www.linezing.com/router.php'); //登录后跳转的网址
//curl_setopt($ch, CURLOPT_COOKIEFILE,$cookiefile); 原先我在这里也记录cookie了,但事实上,我这样做之后,反而会把第一次登录时的cookie覆盖了。。。郁闷
//curl_setopt($ch, CURLOPT_COOKIEJAR,$cookiefile);
curl_setopt($ch, CURLOPT_REFERER, 'http://www.linezing.com/');
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_COOKIESESSION,1);
$res = curl_exec($ch);

curl_setopt($ch, CURLOPT_URL, 'http://tongji.linezing.com/welcome.html'); //welcome页还会再判断是否登录,如果没有登录,会是一段JS跳到www.linezing.com
//curl_setopt($ch, CURLOPT_COOKIEFILE,$cookiefile);
//curl_setopt($ch, CURLOPT_COOKIEJAR,$cookiefile);
curl_setopt($ch, CURLOPT_REFERER, 'http://www.linezing.com/');
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_COOKIESESSION,1);
curl_exec($ch);

curl_setopt($ch, CURLOPT_URL, 'http://tongji.linezing.com/mystat.html');
curl_setopt($ch, CURLOPT_COOKIEFILE,$cookiefile);
curl_setopt($ch, CURLOPT_COOKIEJAR,$cookiefile);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_COOKIESESSION,1);
$res = curl_exec($ch); //这里获取返回值,我是想看一下是不是正确。。。
curl_close($ch);
echo htmlSpecialChars( $res );
因为没写什么,所以就注释写的详细了一点
转:智能APP的Banner

转:智能APP的Banner

这是最近的一个小热点,即在网页的meta里加入一行代码:<meta name="apple-itunes-app" content="app-id=1234567890"/> ,红色区域就是你的APP的ID
不知道,有两个APP的情况下,即加入两条的时候会怎么样?
不过,目前该功能只支持Safari,而且是IOS上的Safari,看来IOS6中,对safari加了好多功能啊。

记录下这个,也是做个笔记。

soap,又见SOAP

soap,又见SOAP

其实,不知道从几年前起,我就不再碰SOAP了,虽然在自己的项目里也悄悄的实现了一个soap。但那是我偷懒,其实我是将我最常用的一些工具封装了一个类,但是其他人需要调用,于是乎我利用soap,直接将这个类发布了,也就相当于实现了soap的一些最常用的方法。果然是个偷懒的好技巧。

提起这个,是最近有一些朋友来问我soap的相关问题,当初我对soap的使用也就是最多在header里做了一个验证。本来phptext.cn(小猪的网站)上有介绍的这篇文章,但后来他的网站好象崩过一次,数据就没有了。在此,我再介绍 一些文章,以前其实也都介绍过:
1、http://rdc.taobao.com/blog/qa/?p=6023
2、http://www.laruence.com/2010/03/26/1365.html
第一篇是淘宝qa团队,教你怎么使用soapUI进行soap接口测试,第二篇是风雪之隅写的关于soap怎么使用soapHeader之类的文章

当然在我以前的博客里也记录过一些创建soap的文章,比如这个:关于SOAP的几篇文章 ,也可以了解一下。
说实话,现在我真的忘了soap了。