php解析bt,PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解

本文实例讲述了PHP基于闭包思想实现的torrent文件解析工具。分享给大家供大家参考,具体如下:

PHP对静态词法域的支持有点奇怪,内部匿名函数必须在参数列表后面加上use关键字,显式的说明想要使用哪些外层函数的局部变量。

function count_down($count)

{

return $func = function()

use($count,$func)

{

if(--$count > 0)

$func();

echo "wow\n";

};

}

$foo = count_down(3);

$foo();

我本来是想这样的。但是不行,会在第7行调用$func的时候报错。

错误是Fatal error: Function name must be a string in - on line 7

反复试验后发觉,外部的匿名函数应该通过引用传值传给内部,否则是不行的:

function count_down($count)

{

return $foo = function()

use(&$count,&$foo)

{

echo $count."\n";

if(--$count > 0)

$foo();

};

}

$foo = count_down(4);

$foo();

像上面这样写就对了。

下面是另一种方法:

function count_down_again($count)

{

return function()use($count)

{

printf("wow %d\n",$count);

return --$count;

};

}

$foo = count_down_again(5);

while($foo() >0);

不过,这段代码有点小错误。编译虽然没错,但是$foo函数每次返回的都是4.

也就是use关键字看上去像是支持静态词法域的,在这个例子上,它只是对外层函数使用的变量作了一个简单拷贝。

让我们稍微修改一下,把第3行的use($count)改为use(&$count):

function count_down_again($count)

{

return function()use(&$count)

{

printf("wow %d\n",$count);

return --$count;

};

}

$foo = count_down_again(5);

while($foo() >0);

这样才正确。

我个人使用的方式是基于类的,做成了类似下面的形式:

class Foo

{

public function __invoke($count)

{

if($count > 0)

$this($count - 1);

echo "wow\n";

}

}

$foo = new Foo();

$foo(4);

这样做的行为也是正确的。

这样不会像前一个例子那样失去了递归调用的能力。

虽然这是一个类,但是只不过是在手动实现那些支持闭包和静态词法域的语言中,编译器自动实现的动作。

其实今天早上,我本来准备用类scheme的风格写一个解析器的。可能稍微晚点吧。scheme风格的函数式编程是这样的:

function yet_another_count_down($func,$count)

{

$func($count);

if($count > 0)

yet_another_count_down($func,$count - 1);

}

yet_another_count_down(function($var){echo $var."\n";},6);

它不是很依赖静态词法域,虽然scheme对静态词法域的支持还是很不错的。它主要还是利用了first-class-function。当然,这也是一种典型的闭包。

我实现的torrent解析工具的代码如下:

$file_name = '1.torrent';

$file = fopen($file_name,'r');

$nil = new Parser($file);//构造解析器

$nil = $nil();//进行解析

$pos = ftell($file);

echo '读取到文件位置'.sprintf('0x%08X',$pos)."\r\n";

fseek($file,0,SEEK_END);

echo '还剩下'.(ftell($file) - $pos).'字节未读取'."\r\n";

if(!feof($file))

{

echo '文件还未结束,再读一个字符:';

$ch = fgetc($file);

if(is_string($ch) && ereg('\w',$ch))

{

echo $ch."\r\n";

}

else

{

printf('0x%02X',$ch);

echo "\r\n";

}

echo '现在的文件位置是'.sprintf('0x%08X',ftell($file))."\r\n";

echo '文件'.(feof($file)?'已结束':'还未结束')."\r\n";

}

fclose($file);//解析器后面不再工作了,此时可以释放文件指针了。

$info = @$nil['value'][0]['info'];

if(!$info)

{

echo '这是一个有效的B-Encoding文件,但它不是一个有效的种子文件';

exit();

}

$name = $info['name.utf-8'] ?$info['name.utf-8']:$info['name'];

if(!$name)

{

echo '这是一个有效的B-Encoding文件,但它不是一个有效的种子文件';

exit();

}

echo $name."\r\n";

if($info['files'])

{

$index = 0;

foreach($info['files'] as $f)

{

$index += 1;

$path = $f['path.utf8'] ?$f['path.utf8'] :$f['path'];

if(!$path)

{

echo '文件列表中的第'.$index."个文件不含目录\r\n";

continue;

}

if(0 === strpos($path[0],"_____padding_file_"))continue;

$under_folder = false;

foreach($path as $item)

{

if($under_folder)

{

echo '/';

}else{

$under_folder = true;

}

echo $item;

}

echo "\r\n";

}

}

else

{

echo "仅有一个文件\r\n";

}

class Parser

{

private $_file;

public function __construct($file)

{

$this ->_file = $file;

}

public function __invoke($parent = array())

{

$ch = $this ->read();

switch($ch)

{

case 'i':

{

$n = $ch;

while(($ch = $this ->read()) != 'e')

{

if(!is_numeric($ch))

{

echo '在';

echo sprintf(

'0x%08X',ftell($this ->_file));

echo '解析数字时遇到错误',"\r\n";

echo '在i和e之间不应该出现非数字字符'."\r\n";

echo '意外的字符'.sprintf('0x%02X',$ch);

exit();

}

else

{

$n .= $ch;

}

}

$n += 0;

$offset = count($parent['value']);

$parent['value'][$offset] = $n;

return $parent;

}

break;

case 'd':

{

$node = array();

//这个$node变量作为字典对象准备加入到$parent的孩子节点中去

//$node['type'] = 'd';

while('e' != ($tmp = $this($node)))

{//每次给$node带来一个新孩子

$node = $tmp;

}

$child_count = count($node['value']);

if($child_count % 2 != 0)

{

echo '解析结尾于';

echo sprintf('0x%08X',ftell($this ->_file));

echo '的字典时遇到错误:'."\r\n";

echo '字典的对象映射不匹配';

exit();

}

$product = array();

for($i = 0; $i < $child_count; $i += 2)

{

$key = $node['value'][$i];

$value = $node['value'][$i + 1];

if(!is_string($key))

{

echo '无效的字典结尾于';

echo sprintf('0x%08X',ftell($this ->_file));

echo ":\r\n";

echo '解析[k => v]配对时遇到错误,k应为字符串';

exit();

}

$product[$key] = $value;

}

/*

* 思想是这样的:子节点想要加入父节点时,

* 往父节点的value数组添加。

* 当父节点收集好所需的信息后,

* 父节点自身再从它的value节点整合内容

* 对于字典和列表统一这样处理会大大降低代码量

*/

$offset = count($parent['value']);

$parent['value'][$offset] = $product;

return $parent;

}

break;

case 'l';

{

$node = array();

while('e' != ($tmp = $this($node)))

{

$node = $tmp;

}

$offset = count($parent['value']);

$parent['value'][$offset] = $node['value'];

return $parent;

}

break;

case 'e':

return 'e';

break;

default:

{

if(!is_numeric($ch))

{

$this ->unexpected_character(

ftell($this ->_file) - 1,$ch);

}

$n = $ch;

while(($ch = $this ->read()) != ':')

{

$n .= $ch;

if(!is_numeric($n))

{

unexpected_character(

ftell($this ->_file) - 1,$ch);

}

}

$n += 0;

$str = '';

for(; $n > 0; --$n)

{

$str .= $this ->read();

}

$offset = count($parent['value']);

$parent['value'][$offset] = $str;

return $parent;

}

break;

}

}

/*

* read函数包裹了$this ->_file变量

*/

function read()

{

if(!feof($this ->_file))

{

return fgetc($this ->_file);

}else{

echo '意外的文件结束';

exit();

}

}

/*

* unexpected_character函数接收2个参数

* 它用于指明脚本在何处遇到了哪个不合法的字符,

* 并在返回前终止脚本的运行。

*/

function unexpected_character($pos,$val)

{

$hex_pos = sprintf("0x%08X",$pos);

$hex_val = sprintf("0x%02X",$val);

echo 'Unexpected Character At Position ';

echo $hex_pos.' , Value '.$hex_val."\r\n";

echo "Analysing Process Teminated.";

exit();

}

}

?>

这里很有趣的是,明明我对文件调用了fseek($file,0,SEEK_END);移动到文件末尾了,但是feof还是报告说文件没有结束,并且fgetc返回一个0,而没有报错。但是此时文件实际上已经到末尾了。

希望本文所述对大家PHP程序设计有所帮助。 ad51e517755f8fd6a7ec83ced4ecfaf3.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/550984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php excel header,【IT专家】PHP生成excel,方法一-header生成

本文由我司收集整编&#xff0c;推荐下载&#xff0c;如有疑问&#xff0c;请与我司联系PHP生成excel&#xff0c;方法一:header生成2018/02/09 444 public function export_order() { //搜索条件$where ‘where 11 ‘; $consignee I(‘consignee’); if($consignee){ $where…

discuz php源码,Discuz7 php源码,该如何解决

PHP code$_value) { $_key{0} ! _ && $$_key daddslashes($_value);}}//过滤$_FILES,也就是添加引用if (!MAGIC_QUOTES_GPC && $_FILES) {$_FILES daddslashes($_FILES);}//初始化一些变量$charset $dbs $dbcharset $forumfounders $metakeywords $extr…

php显示html表单内容,HTML表单是什么?HTML表单内容的详细介绍(附代码)

HTML 表单是用于搜集不同类型的用户输入的&#xff0c;表单是一个包含表单元素的区域&#xff1b;表单元素是允许用户在表单中(比如&#xff1a;文本域、下拉列表、单选框、复选框等等)输入信息的元素&#xff1b;表单使用表单标签()定义。一、介绍1.表单概念&#xff1a;表单最…

matlab分数约分,分母

3.10c语言自学2021-03-10 19:51:18https://s4.51cto.com/images/blog/202103/10/489602174c3d2e1deb2f94bc7944366c.png?x-oss-processimage/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk)term-termxx/(n(…

php中metadata,模型元数据(Models Metadata)

To speed up development Phalcon\Mvc\Model helps you to query fields and constraints from tables related to models. To achieve this, Phalcon\Mvc\Model\MetaData is available to manage and cache table metadata.Phalcon\Mvc\Model 能帮助你查询字段从表关联到模型和…

matlab函数冒号代表的意思,MATLAB中冒号运算符的含义

实际上a&#xff1a;b生成一个向量.您可以仅将其用作索引,因为(…)也接受列表,例如octave-3.0.3:10> a [1,4,7]a 1 4 7octave-3.0.3:11> b [1,4,9,16,25,36,49]b 1 4 9 16 25 36 49octave-3.0.3:12> b(a) # gets [b(1), b(4), b(7)]ans 1 16 49现在,a&#xff1a;b&…

ubuntu安装matlab空间不足,Ubuntu安装Matlab方法及命令

操作系统&#xff1a;Ubuntu 10.04matlab版本&#xff1a;Mathworks.Matlab.R2010a.UNIX注意&#xff1a;软件安装后会占用大量硬盘空间&#xff0c;约4G.软件Mathworks.Matlab.R2010a.UNIX一、软件安装1.右键 Mathworks.Matlab.R2010a.UNIX.iso &#xff0c;解压缩到此处。2.在…

java怎么让1的数据2可以拥有,【如何让代码变“高级”(二)】-这样操作值得一波666(Java Stream)(这么有趣)...

“致"高级"工程师(BUG工程师)一颗折腾的心原创不易&#xff0c;点个赞&#x1f497;&#xff0c;支持支持开发中的代码在开发中的代码是不是很常见这样的代码&#xff1a;这样的?for循环取元素取值List szUserList new ArrayList<>();for (User user : userL…

网易云信短信接口java,调用网易云短信验证码接口Demo

最近在开发新的项目&#xff0c;因为需要使用验证码验证这个功能。我选择的是 网易云 的验证码接口。免费使用20条。并且在正式使用的过程中&#xff0c;对比了很多家以后&#xff0c;感觉还是他家的便宜。因为随着你购买的短信包数量的增加&#xff0c;价钱会相应的降低&#…

php+页面加载进度,基于jQuery实现模拟页面加载进度条_jquery

因为我们无法通过任何方法获取整个页面的大小和当前加载了多少&#xff0c;所以想制作一个加载进度条的唯一办法就是模拟。那要怎么模拟呢&#xff1f;我们知道&#xff0c;页面是从上往下执行的&#xff0c;也就是说我们可以大致估算出在页面的某个位置加载了多少&#xff0c;…

php eurl.axd,Http异常eurl.axd出错信息解决方法

您可能感兴趣的话题&#xff1a;IIS核心提示&#xff1a;在IIS6中同时启用了ASP.NET 2.0 和 ASP.NET 4.0 后&#xff0c;网站程序可能会出现如下错误&#xff1a;“ System.Web.HttpException: Path ‘//eurl.axd/‘ was not found. ”在IIS6中同时启用了ASP.NET 2.0 和 ASP.NE…

php radio js,如何使用JavaScript设置radio选中的示例

在页面数据绑定时&#xff0c;经常会遇到给radio设置选中&#xff0c;以下是我写的js方法&#xff0c;经测试可以使用。欢迎拍砖是否是否$(function(){$("#btn1").click(function(){$("input[namerdo1]").eq(0).attr("checked","checked&qu…

oracle 列 连续,oracle中某列连续相同值的记录数统计(一个简单的例子)

oracle中某列连续相同值的记录数统计(一个简单的例子) row_number() OVER (PARTITION BY COL1 ORDER BY COL2) 表示根据COL1分组&#xff0c;在分组内部根据 COL2排序&#xff0c;而此函数计算的值就表示每组内部排序后的顺序编号(组内连续的唯一的).与rownum的区别在于&#x…

oracle ora31633,ORA-31633: unable to create master table

ORA-31626: job does not existORA-31633: unable to create master table "NEWCOURSE.JOB_48"ORA-06512: at "SYS.DBMS_SYS_ERROR", line 95ORA-06512: at "SYS.KUPV$FT", line 1020ORA-00955: name is already used by an existing object同步…

a表剔除b表 oracle,删除a表中和b表相同的数据

删除a表中和b表相同的数据删除a表中和b表相同的数据软件环境&#xff1a;1、Windows NT4.0ORACLE 8.0.42、ORACLE安装路径为&#xff1a;C:&#xff3c;ORANT问题提出&#xff1a;1、在做数据转储业务的时候&#xff0c;如果发生操作错误&#xff0c;有可能出现主表和副表中都有…

nifi将hive同步到oracle,NiFi使用总结 一 hive到hive的PutHiveStreaming processor和SelectHiveQL...

我说实话&#xff0c;NiFi的坑真的挺多的。。。1、PutHiveStreaming processor的使用该控制器配置需要hive启用事物&#xff1b;且目前只支持orc格式&#xff0c;且建表需要分桶&#xff0c;开启事务等&#xff0c;建表示例如下&#xff1a;create tabletest_trancaction(user_…

rds oracle,Amazon RDS Oracle数据库托管

您可通过两种不同的许可模式运行 Amazon RDS for Oracle&#xff0c;即“附带许可”和“使用自有许可 (BYOL)”。在“附带许可”服务模型中&#xff0c;您无需单独购买 Oracle 许可&#xff1b;Oracle 数据库软件软件由 AWS 提供授权许可。“附带许可”的起价为 0.04 USD/小时&…

oracle监听系统账号,linux 下 Oracle 监控sysdba用户登陆

在unix平台的Oracle数据库产品中&#xff0c;每次当用户以sysdba权限登陆数据库时&#xff0c;系统就自动创建一个名为ora_.aud的文件&#xff0c;该文件默认在$ORACLE_HOME/rdbms/audit目录下。该文件的自动创建根本不需要系统打开审计功能。在这个文件中记录了连接的用户&…

linux目录下有斜杠,Windows和Linux路径中斜杠/和反斜杠\ 的区别

Unix使用斜杆/ 作为路径分隔符&#xff0c;而web应用最新使用在Unix系统上面&#xff0c;所以目前所有的网络地址都采用 斜杆/ 作为分隔符。Windows由于使用 斜杆/ 作为DOS命令提示符的参数标志了&#xff0c;为了不混淆&#xff0c;所以采用 反斜杠\ 作为路径分隔符。所以目前…

linux密码带星号,Linux下实现输入密码以星号显示

#include #include #include #include #include #include char Getch(){int c 0;int res 0;struct termios org_opts, new_opts;res tcgetattr(STDIN_FILENO, &org_opts); //用于获取与终端相关的参数if(res -1){printf("tcgetattr error! Error code: %d&#xf…