bianbian coding life

便便代码人生: 关注技术, 翻译文档, 偶尔动动手

Archive for the 'php' Category

php

[原] php读取二进制流(C语言结构体struct数据文件)

Posted by bianbian on 2008-04-20 01:08


本文Tags: , , , , , , , ,

尽管php是用C语言开发的,不过令我不解的是php没有提供对结构体struct的直接支持。
不过php提供了pack和unpack函数,用来进行二进制数据(binary data)和php内部数据的互转:

  1. string pack ( string $format [, mixed $args [, mixed $...]] )
  2. //Pack given arguments into binary string according to format.
  3.  
  4. array unpack ( string $format, string $data )
  5. //Unpacks from a binary string into an array according to the given format.

其中,$format跟perl里的pack格式类似,有如下一些(中文是我加的,有不准确的欢迎提出):
a NUL-padded string,即“\0”作为“空字符”的表示形式
A SPACE-padded string,空格作为“空字符”的表示形式
h Hex string, low nibble first,升序位顺序
H Hex string, high nibble first,降序位顺序
c signed char,有符号单字节
C unsigned char,无符号单字节
s signed short (always 16 bit, machine byte order)
S unsigned short (always 16 bit, machine byte order)
n unsigned short (always 16 bit, big endian byte order)
v unsigned short (always 16 bit, little endian byte order)
i signed integer (machine dependent size and byte order)
I unsigned integer (machine dependent size and byte order)
l signed long (always 32 bit, machine byte order)
L unsigned long (always 32 bit, machine byte order)
N unsigned long (always 32 bit, big endian byte order)
V unsigned long (always 32 bit, little endian byte order)
f float (machine dependent size and representation)
d double (machine dependent size and representation)
x NUL byte,实际使用的时候作为跳过多少字节用,很有用
X Back up one byte,后退1字节
@ NUL-fill to absolute position,实际使用的时候作为从开头跳到某字节用,很有用

实际使用发现:C里的“\0”(即字符串终止符)在php里并不是终止符,而是作为了字符串的一部分。因此,必须对“\0”进行特殊处理,才能进行struct和php内部数据的完美互转。比如 char name[10]; 如果实际数据是“62 69 61 6E 00 62 69 61 6E 00”,在C语言里第5个位置有终止符,name应该是“bian”;而用了unpack转换以后在php里的name却是“bian\0bian\0”。
一开始我用了strpos函数找到“\0”的位置,然后进行substr截取:

  1. $name = substr($name, 0, strpos($name, "\0"));

不过很Faint的事情发生了,不知道是strpos的bug还是substr的bug(其实测试一下就知道,懒得试),有些字符串没问题,有些字符串却只能得到空值(即$name == ”)。很是郁闷,后来找了个strtok函数,这下没有问题了:

  1. $name = strtok($name, "\0");

难为大家看了那么多,下面写个完整的php读取二进制数据流(C语言结构体struct数据)文件的示例代码:
首先是C的struct定义示例,为了演示,我就写个简单点的,实际对照上面那个$format格式表应该没有问题:

  1. struct BIANBIAN {
  2.     char name[10];
  3.     char pass[33];
  4.     int  age;
  5.     unsigned char flag;
  6. };

比如有个“bianbian.org”文件,内容就是上面的N个BIANBIAN结构体构成的。读取的php代码:

  1. //下面根据struct确定$format,注意int类型跟机器环境有关,我的32位Linux是4个长度
  2. $format = 'a10name/a33pass/iage/Cflag';
  3. //确定一个struct占用多少长度字节,如果只是读取单个结构体这是不需要的
  4. $length = 10 + 33 + 4 + 1;
  5. //也可以用fopen + fread + fclose,不过file_get_contents因为可以mmap,效率更高
  6. $data = file_get_contents('bianbian.org', 'r');
  7. for ($i = 0, $c = strlen($data); $i < $c; $i += $length) {
  8.     $bianbian = unpack("@$i/$format", $data);
  9.     //reference传递是php 5才支持的,如果用php4,得用其他办法
  10.     foreach ($bianbian as &$value) {
  11.         if (is_string($value)) {
  12.             $value = strtok($value, "\0");
  13.         }
  14.     }
  15.     print_r($bianbian);
  16. }
  17. //输出为array,即类似:
  18. Array
  19. (
  20.     [name] => 'bianbian'
  21.     [pass] => 'bianbian.org'
  22.     [age]  => 100
  23.     [flag] => 0
  24. )
  25. ...

pack应该跟unpack相反。

标签: , , , , , , , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | 4 Comments »

[原] php 5读取非UTF-8编码的文件或网页乱码的解决

Posted by bianbian on 2008-04-09 09:16


本文Tags: , , ,

php 5的流读取函数好像默认编码是UTF-8,以前在php 4里直接file_get_contents()读取gb2312编码的正常,到了5就乱码了。网上的解决办法说抓取后用iconv()转码。看后我就觉得不对劲:一个是不一定编译了iconv库,更大的问题是编码都跟流转换的时候有关(如果用了iconv实际上php转了两次码:流 -> UTF-8 -> GB2312):这不是白忙乎了吗?

仔细看了下php的文档(不知道大家都是怎么写代码的,其实文档上很清楚啊),上面关于fopen()及file_get_contents()都提到了“默认是UTF-8,但是用户可以用stream_default_encoding()或者用户自定义上下文属性改变编码”(If unicode semantics are enabled, the default encoding of the read data is UTF-8. You can specify a different encoding by creating a custom context or by changing the default using stream_default_encoding().)。于是用stream_default_encoding(’gb2312′);测试:但是faint的是,这个函数不存在?!似乎php 6才支持。不过天无绝人之路,还有“用户自定义上下文属性”可以用。

经过更仔细的看文档,最后解决了这个问题:

  1. //设置流的编码格式,这是文件流(file),如果是网络访问,file改成http
  2. $opts = array('file' => array('encoding' => 'gb2312'));
  3. $ctxt = stream_context_create($opts);
  4. file_get_contents(文件名, FILE_TEXT, $ctxt);
标签: , , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | No Comments »

[原] PHP bug or Slackware bug? –enable-fastcgi always failed.

Posted by bianbian on 2007-12-29 04:07


本文Tags: , , ,

On Slackware 12.0, compile php to fastcgi alwayes failed, only get CGI:

./configure --enable-fastcgi --enable-force-cgi-redirect

./php --version
PHP x.x.x (cgi)
....

so use spawn-fcgi (lighttpd) always get exit.

OH MY GOD…. I tried from php 4.3.0 - php 5.2.0, only php 4.4.4 can get fastcgi:

./php --version
PHP 4.4.4 (cgi-fcgi) (built: Dec 29 2007 03:55:36)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies

why??????????????????????

标签: , , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | No Comments »

[原]php的base64解码函数

Posted by bianbian on 2007-03-19 04:22


本文Tags: ,

调用 base64_decode() 就行了。
我承认我很BT地觉得是不是php的这个函数有问题,导致我原先提到的问题产生:php处理base64编码和Unicode客户端交互的问题
于是我照着某文档,重新实现了一下。结果发现是一样的(废话)。-___-!!
我承认我错了,当然前面那个问题不是php的原因。不过既然已经写了,我就贴一下吧。至少base64解码俺是会了。哈哈,对了,还有C语言实现的base64解码,下次也来贴一下。

  1. //php功能等同于base64_decode(),所以没有任何实际用途
  2. function my_base64_decode(&$str)
  3. {
  4.     $Bstr_base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  5.     $len = strlen($str);
  6.     if ($len % 4 !=0)
  7.         return '';
  8.     $res = $bins = '';
  9.     for ($i = 0; $i < $len; $i++)
  10.     {
  11.         $nChar = substr($str, $i, 1);
  12.         if ($nChar == '=')
  13.             break;
  14.         $oldValue = strpos($Bstr_base64, $nChar);
  15.         $binValue = substr('000000' . decbin($oldValue), -6);
  16.         $bins .= $binValue;
  17.        
  18.         if (strlen($bins) >= 8)
  19.         {
  20.             $deChar = substr($bins, 0, 8);
  21.             $bins = substr($bins, 8);
  22.             $res .= chr(bindec($deChar));
  23.         }
  24.     }
  25.    
  26.     return $res;
  27. }
标签: ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | 3 Comments »

[原]php处理base64编码和Unicode客户端交互的问题

Posted by bianbian on 2007-03-19 01:14


本文Tags: , , , ,

最近才对编码问题真正深入研究。因为我碰到了这个问题:客户端传过来的是对UTF-16的base64编码,而php没有办法正确解码。
有关编码,可以先看看这篇文章:字符,字节和编码
关于UTF-8和UTF-16,可以参考:谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词
有关URL的base64处理,可以参考这篇:通过 URL 传递 base64 编码参数的问题
摘录如下:

一般情况下,URL 中的参数应使用 url 编码规则,即把参数字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。但是对于带有中文的参数来说,这种编码会使编码后的字符串变得很长。如果希望有短一点的方式对参数编码,可以采用 base64 编码方式对字符串进行编码,但是 base64 编码方式不能处理 JavaScript 中的中文,因为 JavaScript 中的中文都是以 UTF-16 方式保存的。而 base64 只能处理单字节字符,所以不能直接用 base64 对带有中文的 JavaScript 字符串进行编码。但是可以通过 utf.js 这个程序中提供的 utf16to8 来将 UTF-16 编码的中文先转化为 UTF-8 方式,然后再进行 base64 编码。这样编码后的字符串,在传递到服务器端后可以直接通过 base64_decode 解码成 UTF-8 的中文字符串。但是还有个问题需要注意。base64 编码中使用了加号(+),而 + 在 URL 传递时会被当成空格,因此必须要将 base64 编码后的字符串中的加号替换成 %2B 才能当作 URL 参数进行传递。否则在服务器端解码后就会出错。

不过,我对上述的“而 base64 只能处理单字节字符”有疑问,这是base64的标准规定,还是php的缺陷?
如果是php的缺陷,我想应该能写一个针对UTF-16的base64解码函数。这样就不需要客户端来进行UTF16转成UTF8了。

补充:
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
上面第二篇摘录如下:

UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?

Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:

在UCS编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符”ZERO WIDTH NO-BREAK SPACE”。

这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。

UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

Windows就是使用BOM来标记文本文件的编码方式的。

也就是说,base64编码必须针对单字节。从这个角度说,是不能对UTF-16的字串进行base64编码操作的。难怪php解码后得到的结果不一致。原来问题不是出在php上。

我又看了客户端base64编码部分的代码,确实是客户端的问题。客户端用了“(const BYTE*)sSrcBuf ”把UTF-16的双字节强制转为BYTE流之后进行base64编码,结果是错误的。难怪我解码以后英文字符之间都有莫名奇妙的“空格”(实际是“00”,但是记事本看网页源代码上的“00”显示成空格)。
实际上是这样的:比如“ABC”,UTF-8字节流是“41 42 43”。而UTF-16则是“41 00 42 00 43 00”,所以“(const BYTE*)sSrcBuf ”之后,“00”就被作为单独的一个字节进行了转码,所以解码以后就还原为UTF-8的字节流“41 00 42 00 43 00”,这时候跟初始的“41 42 43”已经不一致了(print的时候只能打出“A”)。难怪我的SQL会报出“ERROR: unterminated quoted string at or near “‘A” at character”的错,因为数据库读取SQL读到“00”就认为结束了。
这时候我冒出一个想法,要是服务器端解码以后再强制把字节流当做UTF-16转为UTF-8,结果不就一样了吗?
事实证明我的想法是正确的:

  1. $utf16 = chr(hexdec('FF')) . chr(hexdec('FE')) . base64_decode($_GET[abc]);
  2. print(iconv('UTF-16', 'UTF-8', $utf16));
  3. //输出"ABC",这是对的!

最后还有一个问题,UTF-16的BOM不一定是FF FE,也可能是FE FF。这怎么办?要是有ASCII字符还好办,两个两个的找,看“00”是在前还是在后。要是双字节都有内容呢?举上面文章的例子:“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?
这个问题单纯改服务器端php没有办法解决。最终的解决办法只有两个:
1)如上面文章所说,客户端UTF-16先转成UTF-8再base64编码,再提交;显示的时候转回UTF-16
2)客户端可以基于UTF-16的字节流base64编码,但是提交的时候要带上“BOM”,即指明是“FFFE”,还是“FEFF”

当然,如果能确定客户端的Unicode的BOM方式,上面的2)也可以不用。
直接在服务器端加上BOM标记进行转码即可。
好像Windows下都是FFFE的,所以可以直接用这个:

  1. //加上UTF-16 BOM的base64解码(客户端是根据FFFE的Unicode base64的)
  2. function get_utf8_from_base64_utf16(&$str)
  3. {   
  4.     $utf16 = chr(hexdec('FF')) . chr(hexdec('FE')) . base64_decode($str);
  5.     return iconv('UTF-16', 'UTF-8', $utf16);
  6. }
标签: , , , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | 2 Comments »

[原]超级简单访问PostgreSQL的小型php类

Posted by bianbian on 2007-03-17 07:55


本文Tags: , , ,

实在受不了ADOdb之类的庞大库了,还有网上下的几个库也超级难用,我自己写了一个。只当简单的用,事务之类的我还没有用到,所以没有写进去。感兴趣的可以补充。好像里面的函数有些要php 4.2以上的。
使用示例:

  1. <?php
  2. $db = & new PostgreSQL();
  3. //single record:
  4. $record = & $db->query_first("SELECT * FROM table WHERE id=xxx");
  5. //many lines:
  6. if ($records = & $db->query_all("SELECT * FROM table WHERE id>xxx")) {
  7.     foreach ($records as &$record) {
  8.         ...
  9.     }
  10. }
  11. $db->close();
  12. ?>

类的代码:
Read the rest of this entry »

标签: , , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | 1 Comment »

[原]php用UTF-8编码总结

Posted by bianbian on 2007-03-16 06:25


本文Tags: , ,

前几天说的PostgreSQL ERROR原来是php文件没有UTF-8编码导致的问题。
以前说过如果JS文件不是UTF8会在IE有bug,所以JS代码也要用UTF-8。
还有数据库也都要用UTF-8。
php用UTF-8总结:
1) php文件本身必须是UTF-8编码。不像Java会生成class文件,避免这个问题。
2) php要输出头:header(”Content-Type: text/html; charset=UTF-8″);
3) meta标签无所谓,有header所有浏览器就会按header来解析
4) 所有外围都得用UTF8,包括数据库、*.js、*.css(CSS影响倒不大)
5) 最后,php本身不是Unicode的,所有substr之类的函数得改成mb_substr(需要装mbstring扩展);或者用iconv转码(基本上的linux都装了,没装的话download、tar、make、make install,很简单的)
特别郁闷的:
文件系统函数不支持UTF-8!

听说php6已经内置Unicode支持,以后试一下。

标签: , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | No Comments »

[原]php版md5,sha-1哪个更快

Posted by bianbian on 2007-03-16 08:01


本文Tags: , ,

一般随机密码都是rand()出来的,我就偷懒直接拿md5,sha-1这些函数来算了:

  1. $s = microtime(true);
  2. for($i = 0; $i < 100000; $i++)
  3.     getRandomPassword();
  4. echo microtime(true) - $s;
  5.  
  6. function getRandomPassword()
  7. {
  8.     ///除去substr和rand的版本:
  9.     //md5(microtime();
  10.     return substr(md5(microtime()), rand(0, 20), 8);
  11. }

经过测试,md5比sha1快(废话 - -):

function with substr and rand only function
md5 1.94637799263 1.79774594307
sha-1 2.12755990028 1.97592806816
标签: , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | No Comments »

[原]发现php几个很方便的函数

Posted by bianbian on 2007-03-16 05:03


本文Tags: , ,

确实好久没接触php了,发现自己很落后了。呵呵,其实几年以前我还是php的超级粉丝。
嗯,突然想起大二时用php做过的一个网站:http://www.geohohai.com。这是我第一份Parttime Job,虽然因为客户的需求不是很明确导致我做的很累,不过从和客户谈、合同编写、项目整体规划、项目文档、美工、编码、部署、验收演示整个流程都是自己一个人搞定,我还是很感谢他们给了我这个长足进步的机会的,也是此后靠IT经济自理的正式开端(以前搞家教,真是的:认真备课教的学生反而没进步;随便瞎吹的反而大进步。真是汗颜)。
我又突然怀念起那一个人的浦口暑假,听着梁静茹,啜着咖啡,为了第一份薪水拼命敲键盘,背后是穿过窗帘的夕阳洒下的斑驳。。。。。为什么回忆总是美好的呢?
刚才点过去突然发现几年过去了,他们已经换成asp了。寒,他们又重复开发了一次,不过基本上的美工和系统架构还是我那个:我也是看链接是asp才发现的。架构也基本上是当时我能想出来最先进的了:所有菜单、所有内容都是后台生成的;菜单配置是程序生成JavaScript代码。嗯,又发神经看了一下JS代码,果然这个还在:
/* 本文件由系统产生,请勿更改 Written by http://dzren.com */
说到http://dzren.com,真是遗憾。原来是“东中人”的意思,做的是我高中母校的校友录,是我学网页编程的练习场所。承蒙大家支持和当时年轻气盛,也有兴旺发达的几年零几个月。可惜没有精力维护下去,现在域名都给别人捡走了。留了几百兆的压缩包在我硬盘上(主要是有上传的照片)。每次看到都伤心一番。。。。
。。。。离题了一大通。。。。
php还是在快速开发方面有很大的优势和进步。这次因为项目转别人的ASP+ACCESS到php+PostgreSQL平台,嗯,重温了一下。发现不用写Java的配置文件,还真是爽啊。
嗯,突然不想写了,贴个Example的代码吧(来自Tore Bjlseth),大家都看得明白:

  1. $url1 = "action=search&interest[]=sports&interest[]=music&sort=id";
  2. $str = parse_str($url1, $output);
  3.  
  4. // Modifying criteria:
  5. $output['sort'] = "interest";
  6.  
  7. $url2 = http_build_query($output);
  8.  
  9. echo "<br>url1: ".$url1;
  10. echo "<br>url2: ".$url2;
  11. /*
  12. Results in:
  13. url1: action=search&interest[]=sports&interest[]=music&sort=id
  14. url2: action=search&interest[0]=sports&interest[1]=music&sort=interest
  15.  
  16. (Array indexes are automatically created.)
  17. */
标签: , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | 2 Comments »

[原]php使用PostgreSQL的编码问题

Posted by bianbian on 2007-03-15 02:43


本文Tags: , , ,

php 5.2 PostgreSQL 8.1
数据库是用UTF8编码建的:
./createdb -E UNICODE xxxx
但是用php插入中文数据的时候会报错:
ERROR: invalid byte sequence for encoding “UTF8″ ….

看字面意思,好像是因为数据库是UTF8,而插入的数据不是UTF8导致的错误。到psql终端下:

# \encoding
UTF8
# show client_encoding;
UTF8

前面一个UTF8是数据库的编码,后一个表示客户端数据编码(就是插入数据的编码)。
怎么解决?

本问题已经解决,请参考这里:php用UTF-8编码总结

标签: , , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Database, Technology, php | No Comments »

[原]解决qmail+iGenus webmail中文UTF-8乱码

Posted by bianbian on 2006-10-11 04:23


本文Tags: , , , ,

最近帮忙解决了一个问题(下面的全凭回忆输入,可能拼写上有问题):很古老的qmail+iGenus webmail系统,邮件若是UTF-8编码则显示乱码(缺省编码为GB2312)。主要解决办法就是利用iconv进行转码:
看看有没有iconv模块,如果没有,需要重新编译php(如果iconv系统里没有装,得先安装iconv:去 http://www.gnu.org/software/libiconv/ 下载,./configure,make,make install),编译php的时候加上 –with-iconv
如果php是静态模块方式和apache绑定的,还得把apache重新编译一下:./configure –activate-module=src/modules/php4/libphp4.a,make clean,make,make install

然后打开iGenus include目录下的Fun_inc.php,找到Decode_mime()函数,在B和Q解码以后加上

  1. if(strtolower($Charset)=='utf-8') $Text = iconv('UTF-8', 'GB2312', $Text);

还有一个地方是正文的编码,打开include目录下的Prev_inc.php,找到Decode_text()函数,为了防止出现错误,复制一下Decode_text(),比如Decode_text_my(),并增加一个参数:$Charset。在函数内解析完数据准备写到文件之前转码:

  1. if(strtolower($Charset)=='utf-8') $buff = iconv('UTF-8', 'GB2312', $buff);

打开iGenus主目录下的prev.php,找到调用Decode_text()的两个地方,改成Decode_text_my(),并增加传递一个参数:$Charset

总结:我看的那个iGenus版本php写得很是糟糕,在那个上面改东西有一种想死的冲动……

标签: , , , ,

遵守创作共用协议,转载请链接形式注明来自http://bianbian.org 做人要厚道

相关日志

Posted in Technology, php | 2 Comments »