unpack和pack来解析字符串
时间:2009-02-12
来源:互联网
今天也来先点东西。
pack和unpack
申明:如果您认为pack,unpack根本不在您的使用范围内或觉得它们没什么用,
那下面的东西大可不看
我们都知道,用于封装数据到字节流,或从字节流解析到我们需要的数据,
我们还知道,通常int占4个字节,char占1个字节,short占2个字节,这些看手册上的pack说明,还有很多很多
比如php跟java进行socket通讯的时候,php发送一段数据给java,(协议自定,这里假定类型10表示获取游戏邮件列表,10000表示获取的id)
socket_write($sock,pack('CN',10,10000),5);
//N和C 手册上都很清楚,分别为unsigned int,char,一共为5个字节
java接受到后,会返回一段数据,从中获得你所需要的,比如java先告诉你返回内容规则如下,1byte,2int
php可以如下获得
$arr=unpack('Csuccess/Nid/Ncount',$data);
这样就完成一次解析过程.
//以下举例都在utf8下完成
这里我们都没有提到字符串的发送,我们知道字符串在字节流里的存储方式是
前2个字节表示字符串的长度,后面表示字符串的具体内容(学过java的应该都了解),2个字节也就限制了发送长度最大为65536
因而我们要发送字符串需要如下:
function pack_str($str){
//如果是gbk,要转成utf8
// $str = iconv('gbk','utf-8',$str);
$utflen = strlen($str);
if ($utflen > 65535) die('too long');
$in .= pack('C2',$utflen>>8,$utflen>>0);
return $in.$str;
}
比如我们要向游戏服务器内发送一个公告:各位,服务器在1小时内重起!
假设java要求这样的格式:协议号:int,标题,内容
我们就可以如下发送:
$in=pack('N',1000);
$in.=pack_utf8('公告');
$in.=pack_utf8('各位,服务器在1小时内重起!');
这样就完成一次发送
同样如果我们需要读取游戏服务器的数据,比如用户资料,也会返回字符串,原理同上
先读2个字节获取长度,再根据长度来获取具体的内容,代码如下:
$crt_str =unpack("C{$crt_str_len}str",$data);
for($ii=1;$ii<=$crt_str_len;$ii++){
$str .= chr($crt_str['str'.$ii]);
}
$str就是我们要获取的中文
但是这样极其烦琐,如果有多个字符串的话,中间又包含了其他数据,比如返回为int,string,int,byte,string这样处理起来相当不便,于是下了下面的函数供大家参考:
[php]<?
/*
由于我的程序经常跟java通信,所以此函数所使用的参数是用java里面的类型来填充的,并且只替换了经常用到的3个类型
C-->b(byte)
n-->s(short)
i-->N(int)
如不习惯或或觉得参数过少,请自行修改
*/
function rs_unpack($parse,$data){
$parselen = strlen($parse);
$parsepos = 0;
$datapos = 0;
$argc = 1;
$ret = array();
while($parsepos<$parselen){
$dostr = false;
$type = substr($parse,$parsepos++,1);
switch($type){
case 'b':
$size = 1;
$argv .= 'C';
break;
case 's':
$size = 2;
$argv .= 'n';
break;
case 'i':
$size = 4;
$argv .= 'N';
break;
case 'Z':
$dostr = true;
/*处理字符传之前的数据*/
$arr = unpack($argv,substr($data,$datapos,$argvlen));
$datapos += $argvlen;
$argvlen = 0;
$argv = '';
$ret = array_merge($ret,$arr);
/*获取要解析的字符串的个数,并移动指针*/
if($parsepos<$parselen) $argc = intval(substr($parse,$parsepos));
if($argc==0) $argc = 1;
while($parsepos<$parselen){
$type = substr($parse,$parsepos,1);
if($type>='0'&&$type<='9'){
$parsepos++;
}else{
break;
}
}
/*获取字符串的命名*/
$namepos = $parsepos;
$type = '';
while($parsepos<$parselen){
$type = substr($parse,$parsepos,1);
$parsepos++;
$namelen++;
if($type=='/') break;
}
$strname = substr($parse,$namepos,$parsepos-$namepos-($type=='/'?1:0));
/*处理各个字符串*/
for($i=0;$i<$argc;$i++){
$str = '';
$crt_len_arr = unpack('nstr_len',substr($data,$datapos,2));
$datapos += 2;
$crt_str_len = $crt_len_arr['str_len'];
$crt_str = unpack("C{$crt_str_len}str",substr($data,$datapos,$crt_str_len));
for($ii=1;$ii<=$crt_str_len;$ii++){
$str .= chr($crt_str['str'.$ii]);
}
$ret = array_merge($ret,array($strname.($argc>1?($i+1):'')=>$str));
$datapos += $crt_str_len;
}
break;
default:
die('parse error');
}
if($dostr) continue;
/*获取数据长度*/
if($parsepos<$parselen) $argc = intval(substr($parse,$parsepos));
if($argc==0){
$argc = 1;
}else{
/*unpack代码限制了只能200*/
if($argc>200) $argc = 200;
}
$argvlen += $argc*$size;
/*移动解析参数指针*/
while($parsepos<$parselen){
$type = substr($parse,$parsepos,1);
$argv .= $type;
$parsepos++;
if($type=='/') break;
}
}
if(!empty($argv)){
$ret = array_merge($ret,unpack($argv,substr($data,$datapos)));
}
return $ret;
}
function pack_str($str){
// $str = iconv('gbk','utf-8',$str);
$utflen = strlen($str);
if ($utflen > 65535) die('too long');
$in .= pack('C2',$utflen>>8,$utflen>>0);
return $in.$str;
}
$in .= pack('C',10);
$in .= pack_str("标题");
$in .= pack('C',10);
$in .= pack_str("内容");
print_r(rs_unpack('bbyte/Zstr/be/Zstrw',$in));
/*比如java发送int,string,string,分别表示协议号,标题,内容
这里用php模拟发送的数据
*/
$in = pack('N',1000);
$in .= pack_str("公告");
$in .= pack_str("服务器在10分钟内重启!");
print_r(rs_unpack('i/Ztitle/Zcontent',$in));
print_r(rs_unpack('i/Z2str',$in));
?>[/php]
需要注意的是
1:很多服务器都会用utf8编码的格式,所以我们的php文件也必须使用同样的编码,否则会出乱码,或其他问题
2:该函数我只处理了4种类行,并且参数用java的类型代替了unpack原来的参数类型(由于我的协议书上都写的java类型,否则我老是要看到int,就用N,看到short就用n多麻烦啊),如需处理其他类型,请自行修改
欢迎大家提出自己的见解,有问题请联系我:
[email protected]
或站内短信
pack和unpack
申明:如果您认为pack,unpack根本不在您的使用范围内或觉得它们没什么用,
那下面的东西大可不看
我们都知道,用于封装数据到字节流,或从字节流解析到我们需要的数据,
我们还知道,通常int占4个字节,char占1个字节,short占2个字节,这些看手册上的pack说明,还有很多很多
比如php跟java进行socket通讯的时候,php发送一段数据给java,(协议自定,这里假定类型10表示获取游戏邮件列表,10000表示获取的id)
socket_write($sock,pack('CN',10,10000),5);
//N和C 手册上都很清楚,分别为unsigned int,char,一共为5个字节
java接受到后,会返回一段数据,从中获得你所需要的,比如java先告诉你返回内容规则如下,1byte,2int
php可以如下获得
$arr=unpack('Csuccess/Nid/Ncount',$data);
这样就完成一次解析过程.
//以下举例都在utf8下完成
这里我们都没有提到字符串的发送,我们知道字符串在字节流里的存储方式是
前2个字节表示字符串的长度,后面表示字符串的具体内容(学过java的应该都了解),2个字节也就限制了发送长度最大为65536
因而我们要发送字符串需要如下:
function pack_str($str){
//如果是gbk,要转成utf8
// $str = iconv('gbk','utf-8',$str);
$utflen = strlen($str);
if ($utflen > 65535) die('too long');
$in .= pack('C2',$utflen>>8,$utflen>>0);
return $in.$str;
}
比如我们要向游戏服务器内发送一个公告:各位,服务器在1小时内重起!
假设java要求这样的格式:协议号:int,标题,内容
我们就可以如下发送:
$in=pack('N',1000);
$in.=pack_utf8('公告');
$in.=pack_utf8('各位,服务器在1小时内重起!');
这样就完成一次发送
同样如果我们需要读取游戏服务器的数据,比如用户资料,也会返回字符串,原理同上
先读2个字节获取长度,再根据长度来获取具体的内容,代码如下:
$crt_str =unpack("C{$crt_str_len}str",$data);
for($ii=1;$ii<=$crt_str_len;$ii++){
$str .= chr($crt_str['str'.$ii]);
}
$str就是我们要获取的中文
但是这样极其烦琐,如果有多个字符串的话,中间又包含了其他数据,比如返回为int,string,int,byte,string这样处理起来相当不便,于是下了下面的函数供大家参考:
[php]<?
/*
由于我的程序经常跟java通信,所以此函数所使用的参数是用java里面的类型来填充的,并且只替换了经常用到的3个类型
C-->b(byte)
n-->s(short)
i-->N(int)
如不习惯或或觉得参数过少,请自行修改
*/
function rs_unpack($parse,$data){
$parselen = strlen($parse);
$parsepos = 0;
$datapos = 0;
$argc = 1;
$ret = array();
while($parsepos<$parselen){
$dostr = false;
$type = substr($parse,$parsepos++,1);
switch($type){
case 'b':
$size = 1;
$argv .= 'C';
break;
case 's':
$size = 2;
$argv .= 'n';
break;
case 'i':
$size = 4;
$argv .= 'N';
break;
case 'Z':
$dostr = true;
/*处理字符传之前的数据*/
$arr = unpack($argv,substr($data,$datapos,$argvlen));
$datapos += $argvlen;
$argvlen = 0;
$argv = '';
$ret = array_merge($ret,$arr);
/*获取要解析的字符串的个数,并移动指针*/
if($parsepos<$parselen) $argc = intval(substr($parse,$parsepos));
if($argc==0) $argc = 1;
while($parsepos<$parselen){
$type = substr($parse,$parsepos,1);
if($type>='0'&&$type<='9'){
$parsepos++;
}else{
break;
}
}
/*获取字符串的命名*/
$namepos = $parsepos;
$type = '';
while($parsepos<$parselen){
$type = substr($parse,$parsepos,1);
$parsepos++;
$namelen++;
if($type=='/') break;
}
$strname = substr($parse,$namepos,$parsepos-$namepos-($type=='/'?1:0));
/*处理各个字符串*/
for($i=0;$i<$argc;$i++){
$str = '';
$crt_len_arr = unpack('nstr_len',substr($data,$datapos,2));
$datapos += 2;
$crt_str_len = $crt_len_arr['str_len'];
$crt_str = unpack("C{$crt_str_len}str",substr($data,$datapos,$crt_str_len));
for($ii=1;$ii<=$crt_str_len;$ii++){
$str .= chr($crt_str['str'.$ii]);
}
$ret = array_merge($ret,array($strname.($argc>1?($i+1):'')=>$str));
$datapos += $crt_str_len;
}
break;
default:
die('parse error');
}
if($dostr) continue;
/*获取数据长度*/
if($parsepos<$parselen) $argc = intval(substr($parse,$parsepos));
if($argc==0){
$argc = 1;
}else{
/*unpack代码限制了只能200*/
if($argc>200) $argc = 200;
}
$argvlen += $argc*$size;
/*移动解析参数指针*/
while($parsepos<$parselen){
$type = substr($parse,$parsepos,1);
$argv .= $type;
$parsepos++;
if($type=='/') break;
}
}
if(!empty($argv)){
$ret = array_merge($ret,unpack($argv,substr($data,$datapos)));
}
return $ret;
}
function pack_str($str){
// $str = iconv('gbk','utf-8',$str);
$utflen = strlen($str);
if ($utflen > 65535) die('too long');
$in .= pack('C2',$utflen>>8,$utflen>>0);
return $in.$str;
}
$in .= pack('C',10);
$in .= pack_str("标题");
$in .= pack('C',10);
$in .= pack_str("内容");
print_r(rs_unpack('bbyte/Zstr/be/Zstrw',$in));
/*比如java发送int,string,string,分别表示协议号,标题,内容
这里用php模拟发送的数据
*/
$in = pack('N',1000);
$in .= pack_str("公告");
$in .= pack_str("服务器在10分钟内重启!");
print_r(rs_unpack('i/Ztitle/Zcontent',$in));
print_r(rs_unpack('i/Z2str',$in));
?>[/php]
需要注意的是
1:很多服务器都会用utf8编码的格式,所以我们的php文件也必须使用同样的编码,否则会出乱码,或其他问题
2:该函数我只处理了4种类行,并且参数用java的类型代替了unpack原来的参数类型(由于我的协议书上都写的java类型,否则我老是要看到int,就用N,看到short就用n多麻烦啊),如需处理其他类型,请自行修改
欢迎大家提出自己的见解,有问题请联系我:
[email protected]
或站内短信
作者: sunceenjoy 发布时间: 2009-02-12
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28