php中的curl几点需要注意的地方

On 十二月 2, 2012, in 技术记录, by pensz

1 CURLOPT_POSTFIELDS中的玄机

curl_setop($ch, CURLOPT_POSTFIELDS, $var); 但不知道内有玄机,php的doc里面有这么一段话。

If value is an array, the Content-Type header will be set to multipart/form-data.

要知道一般POST时,会以application/x-www-form-urlencoded 的 Content-Type POST。而 multipart/form-data 一般用于上传文件。

关于两者的区别,不想读rfc的可以先看看 http://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data

两种情形下POST样例:

application/x-www-form-urlencoded 形式的POST

POST / HTTP/1.1
Host: 192.168.201.197
Accept: */*
Accept-Encoding: gzip
Content-Length: 8
Content-Type: application/x-www-form-urlencoded

url=xxxx

multipart/form-data 格式的POST:

POST / HTTP/1.1
Host: 192.168.201.197
Accept: */*
Accept-Encoding: gzip
Content-Length: 153
Content-Type: multipart/form-data; boundary=—————————-e62c3de4a033

Content-Disposition: form-data; name=”url”

xxx

2 curl中PUT文件

现在都流行RESTful的接口,很多接口要求客户端PUT方式上传文件。在php中,使用curl发起PUT的请求,有两种方法:

curl_setopt($ch, CURLOPT_PUT, 1);
或者
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, ‘PUT’);

当你想使用PUT上传文件时,请不要使用第二种,因为它不会上传文件。

 

Tagged with:  

堆排序

On 九月 12, 2011, in 技术记录, 数据结构和算法, by pensz

堆排序的是经典的排序方法,而堆的存储也非常的巧妙,用n个数据只需要n个结点存储,即采用长度为n+1(+1是为了下标处理的方便)的数组即可。

下面是php的实现。


// 示例数据
$data_input = array(
    null, 34, 10, 23, 58, 20, 49, 11,
);

$data_input = array(
    null, 1, 2, 3, 4, 5, 6, 7,
);

$data_input = array(
    null, 7, 6, 6, 4, 3, 2, 1, 9, 8,
);

// 排序前
var_dump($data_input);

// 堆排序,建大顶堆用来达到从小到大排序的目的
heap_sort($data_input);

// 排序后
var_dump($data_input);

/**
 * 堆排序
 */
function heap_sort(&$heap) {
    $total = count($heap) - 1; // 需要减1,原因是最大下标为 count - 1
    $half = intval($total / 2);

    // 初始化大顶堆
    for ($i=$half; $i>0; $i--) {
        heap_adjust($heap, $i, $total);
    }

    // 取出堆顶,放入最后,然后不断调整保证新的结构为堆
    for ($i = $total; $i > 1; $i--) {
        $temp = $heap[1];
        $heap[1] = $heap[$i];
        $heap[$i] = $temp;
        heap_adjust($heap, 1, $i-1);
    }
}

/**
 * 调整为大顶堆
 */
function heap_adjust(&$heap, $begin, $end) {
    $top_value = $heap[$begin];
    for ($i=2*$begin; $i<=$end; $i*=2) {
        if ($i < $end && $heap[$i] < $heap[$i+1]) $i++; // 右边的结点更加大,选择右边的结点和他们的父结点比较
        if ($top_value >= $heap[$i]) break; //找到了合适的位置,即 topvalue 大于左右的孩子, 为 $i 的父结点很合适
        $heap[$begin] = $heap[$i]; $begin = $i; // 否则,父结点的值为最大值,将$beign处的值设置为$i 结点上的数值,从$i接着调整堆
    }
    $heap[$begin] = $top_value; // 最后的位置保存初始时在堆顶的数据
}
Tagged with:  

败者树

On 九月 10, 2011, in 技术记录, 数据结构和算法, by pensz

很多时候看起来很简单,但是最好自己写一写,这样理解才会更加深刻一些。下面是今天练手写的败者树php实现。


// 测试数据
$data_input = array(
    array(2, 5, 10, 14, 20, 21, 23, 25, 30),
    array(1, 6, 9,  13, 17),
    array(3, 8, 12, 16, 19),
    array(4, 7, 11, 15, 18),
);

// 调用代码示例
k_merge($data_input);
find_x($data_input, 10);

global $b, $k; // $b 为败者树上的叶子结点值, $k为归并的路数量
define('MINVALUE', -65535); // 定义最小值
define('MAXVALUE', 65536); // 定义最大值

/**
 * k 路归并排序
 */
function k_merge($data_input) {
    global $b, $k;
    $k = count($data_input);

    for ($i = 0; $i< $k; $i++) {
        $b[$i] = array_shift($data_input[$i]);
    }

    $loser_tree = array();
    create_loser_tree($loser_tree);

    while ($b[$loser_tree[0]] !== MAXVALUE){
        $key = $loser_tree[0];
        $value = $b[$key];
        print "\n " . $value . " from $key" ;
        $b[$key] = array_shift($data_input[$key]);
        $b[$key] = $b[$key] === null ? MAXVALUE : $b[$key];
        adjust_loser_tree($loser_tree, $key);
    }
}

/**
 * 从 k 段有序序列中找到 第 x 小的数据
 */
function find_x($data_input, $x) {
    global $b, $k;
    $k = count($data_input);

    for ($i = 0; $i< $k; $i++) {
        $b[$i] = array_shift($data_input[$i]);
    }

    $loser_tree = array();
    create_loser_tree($loser_tree);

    for ($x_i = 1; $x_i <= $x - 1; $x_i++) {
        $key = $loser_tree[0];
        $value = $b[$key];
        // print "\n -- $key " . $value ;
        $b[$key] = array_shift($data_input[$key]);
        $b[$key] = $b[$key] === null ? MAXVALUE : $b[$key];
        adjust_loser_tree($loser_tree, $key);
    }

    $key = $loser_tree[0];
    $value = $b[$key];
    print "\n" . $value . "\n";
}

/**
 * 创建败者树
 */
function create_loser_tree(&$loser_tree) {
    global $b, $k;
    $b[$k] = MINVALUE; //下面初始化调整败者树会用到

    for ($i = 0; $i<$k; $i++) {
        $loser_tree[$i] = $k;
    }

    for ($i = $k - 1; $i >=0; $i--) {
        adjust_loser_tree($loser_tree, $i);
        // var_dump(" adjusted $i", $loser_tree);
    }
}

/**
 * 重新调整败者树
 */
function adjust_loser_tree(&$loser_tree, $s) {
    global $b, $k;
    $t = intval(($s + $k) / 2);

    while ($t > 0) {
        if ($b[$s] > $b[$loser_tree[$t]]) {
            $temp = $s;
            $s = $loser_tree[$t];
            $loser_tree[$t] = $temp;
        }
        $t = intval($t / 2);
    }

    $loser_tree[0] = $s;
}
Tagged with:  

php没有原生unicode支持,若没有mb(Multibyte String) 扩展,需要自己写截取中文字符串的代码。保证无乱码的关键在于保证截取的字节数正确,而这个参考编码规则即可。

如 utf8的编码规则:

Char. number range | UTF-8 octet sequence
(hexadecimal) | (binary)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
110xxxxx 意味着 >= 2^7 + 2^6 即 ASCLL值 >= 192 时为两个字节
1110xxxx 意味着 >= 2^7 + 2^6 + 2 ^ 5 即 ASCLL值 >= 224 时为三个字节,以此类推。

按上面的编码规则,可以先取一个字节,通过ASCLL值来判断应该截取字节的字节数。算法如下:

初始化相关数据
while(已经截取的长度 < 要截取的长度) {
临时字符 = 取当前位置后面的1个字节
根据临时字符的ASCLL值判断本次截取的字节长度
最终的字符串 += 从当前位置截取指定长度的字节
当前位置 += 本次截取的字节长度
已经截取的长度 += 本次截取的字节长度
}

返回 最终的字符串

于是有了下面这段经典的代码:

function SubUtf8String($String,$Length) {
    $pos = 0;
    $lenCutted = 0;
    $stringCutted = "";
    $strlen = strlen($String);
    $Length = min($strlen, $Length);

    while ($lenCutted > $Length){
        $StringTMP = substr($String,$pos,1);

        $ascllvalue = ord($StringTMP);

        if ( $ascllvalue >= 224 ){
            $StringTMP = substr($String,$pos,3);
            $pos = $pos + 3;
            $lenCutted = $lenCutted + 3;
        } elseif ( $ascllvalue >= 192 ){
            $StringTMP = substr($String,$pos,2);
            $pos = $pos + 2;
            $lenCutted = $lenCutted + 2;
        } else {
            $pos = $pos + 1;
            $lenCutted = $lenCutted + 1;
        }
        $stringCutted .= $StringTMP;
    }

    if ($stringCutted){
        $stringCutted .= "...";
    }
    return $stringCutted;
}

gbk编码规则:
00-7F 单字节情形
81-FE 40-FE 双字节情形
gb 18030的扩展部分
81-FE 30-39 81-FE 30-39 四字节情形
0×81308130到0xFE39FE39。四字节字符的第一个字节的编码为0×81至0xFE;第二个字节的编码范围为0×30至0×39;第三个字节编码范围为0×81至0xFE;第四个字节编码范围为0×30至0×39

根据上述规则,写出截取gbk编码的函数应该较为简单了。

 

参考资料:

 

Tagged with:  

php中”怪异”的循环

On 六月 9, 2011, in 技术记录, by pensz

前不久在打酱油的时候发现了一个诡异的问题,遍历php数组,竟然结果有问题。具体就是最后一个元素的值和倒数第二个元素的值一样。最后打算去bugs.php.net上搜索一下是不是真的是bug,结果不是bug,有人也报告了这个问题,具体地址请移步至 http://bugs.php.net/bug.php?id=50582 。

问题代码:

Reproduce code:
---------------
$ii = array(1, 2, 3);
foreach ($ii as &$i) echo $i;
foreach ($ii as $i) echo $i;

Expected result:
----------------
123123

Actual result:
--------------
123122

rasmus的解释如下:

It doesn't act weird.  There is no block scope in PHP, so at the end of
the first loop $i is a reference to the last element of $ii and in the
second loop you are now assigning values to that reference which means
you are overwriting the 3rd element of $ii each time through the loop.
That of course means that once you get to the 3rd element of $ii it is
no longer 3 and you see the last value assigned to it, which was 2.

意思就是说:php中没有块作用域,故变量 $i 的作用域不仅仅存在于第一个循环,而是存在与整个代码中,经过第一个循环后,$i 为 $ii最后一个元素的引用;第二个循环的作用就是不断将当前数组中各个元素的值赋值给$ii的最后一个元素,故最后一个元素的值为倒数第二个元素的值。

解决方法就是:

1 第二个循环中不用 $i

2 第一个循环结束后 unset($i)

Tagged with: