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,一经查实,立即删除!

相关文章

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

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

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

因为我们无法通过任何方法获取整个页面的大小和当前加载了多少&#xff0c;所以想制作一个加载进度条的唯一办法就是模拟。那要怎么模拟呢&#xff1f;我们知道&#xff0c;页面是从上往下执行的&#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/小时&…

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

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

telnet服务下载 Linux,linux telnet服务安装包

这是linux telnet服务安装包下载&#xff0c; telnet-client 客户端安装包、telnet-server服务端安装包和xinetd依赖包&#xff0c;本人在linux retHat 32位系统上亲自安装过&#xff0c;若您依旧安装不成功&#xff0c;没关系&#xff0c;在附赠.txt中&#xff0c;打开这个链接…

linux挂载硬盘的分区创建,Linux 新增硬盘、新建分区、格式化硬盘、挂载硬盘的操作...

今天学校有一台机器发现有块硬盘没有挂载&#xff0c;然后叫我挂载一下&#xff0c;这里记录一下 Linux 下新增硬盘分区、格式化硬盘、挂载硬盘的操作。查看当前硬盘首先我们查看一下当前的硬盘配置。使用命令sudo fdisk -l就可以看到目前的硬盘了。可以看到有块空的 sda(这里忘…

Win10 Linux GPT分区方案,win10+Ubuntu 20.04 LTS双系统安装(UEFI + GPT)(图文,多图预警)

win10 安装(已安装的略过)制作启动u盘插入U盘&#xff0c; 运行 rufus-3.10.exe&#xff0c;按照下面选择&#xff0c;然后点击开始&#xff0c;等待完成即可为Ubuntu安装空出分区此电脑->管理->磁盘管理&#xff0c;选择一个磁盘右键选择压缩卷&#xff0c;压缩出50G以上…

linux关闭4750 端口,【ubuntu分享帖】acer 4750G ubuntu安装后的一些设置

本帖最后由 love雨阳 于 2011-11-7 13:27 编辑本帖前提&#xff1a;已经成功安装ubuntu11.10 网卡驱动默认成功1.安装完之后&#xff0c;开机&#xff0c;进入ubunt 第一件事当然是联网,首先&#xff0c;确定你的网络链接方式&#xff0c;如果是路由器自动分配ip的那种应该不用…

linux软件中心替代,Ubuntu 16.04 LTS 将替换 Ubuntu 软件中心

Ubuntu 软件中心在 Ubuntu 16.04 LTS 被移除了。Xenial Xerus 桌面用户会发现非常熟悉的 Ubuntu Software Center 找不到了。GNOME 的软件应用 将会 – 根据当前的计划 – 作为基于 Unity 7 桌面的默认的包管理应用。GNOME 软件应用Ubuntu 将创建新插件来支持新 Software Cente…

linux脚本算术函数,Linux基础之bash脚本编程初级-变量与算术运算

什么是变量&#xff1f;变量最初来源于数学&#xff0c;指的是非固定的值可变化的数通常用拉丁字母表示。在计算机中变量它依旧指的是可变化的数&#xff0c;只不过表现形式有些变化。它指的是变量名所指向的内存空间。以下实验环境均在CentOS7.2进行变量的特点变量分强类型变量…

存储管理实验linux,07-存储管理器实验

S3C2440的存储控制器提供了访问外设所需要的信号&#xff0c;它有如下特性&#xff1a;支持大/小端字节(通过软件选择)&#xff1b;每个BANK的地址空间为128MB&#xff0c;总共1GB(8个BANK)&#xff1b;可编程控制的总线宽度(8/16&#xff0f;32bit)&#xff0c;BANK0只有两种位…

linux bash and,linux bash shell中for的用法and示例

关于linux bash shell中的for语句在linux中shell是必不可少的一部分&#xff0c;但是在bash shell中有while&#xff0c;for&#xff0c;until等循环命令&#xff0c;今天就介绍一下关于for的一些用法。文中任何错误希望大佬们一一指出&#xff0c;不胜感激。bash shell中提供了…

mac利用vscode运行c语言程序,Mac下使用VScode编译配置C/C++程序详细图文教程

在mac上有时候需要编写一些c 或者 c的代码&#xff0c;如果使用 xcode&#xff0c;有时候就显得很笨重&#xff0c;而且运行起来很不方便。而微软提供了一个跨平台的编辑器visual studio code ,这个编辑器很轻量级&#xff0c;而且插件超多&#xff0c;你几乎可以在这个编辑器里…

五邑大学c语言期末考试题,五邑大学 c语言试卷.doc

五邑大学 c语言试卷.doc下载提示(请认真阅读)1.请仔细阅读文档&#xff0c;确保文档完整性&#xff0c;对于不预览、不比对内容而直接下载带来的问题本站不予受理。2.下载的文档&#xff0c;不会出现我们的网址水印。3、该文档所得收入(下载内容预览)归上传者、原创作者&#x…

网页版bpc电波对时_科普向:无需联网却能自动对时的钟表

有一种神秘的钟表&#xff0c;它能够自动对时&#xff0c;却不需要联网&#xff0c;它十分准时&#xff0c;和传说中的的原子钟的误差也仅有几毫秒&#xff0c;它十分小众&#xff0c;却也随处可见&#xff0c;它就是电波表。今天猫头我就跟大家说说这个电波表。首先什么是电波…

动感灯箱制作流程培训_2000多年的灯箱发展史,你知道多少?

灯箱作为日常广告标识业务的重要板块&#xff0c;相信每个标识人都已经非常熟悉了&#xff0c;国内标识人也经常去学习相关新工艺新案例&#xff0c;但其实很多人不知道世界上最早的灯箱可能是中国人制作的哦~接下来就带大家一起揭开灯箱的前世今生&#xff0c;并详细盘点一下如…

c语言程序设计基础1千克,c语言学习知识编程经典编辑题汇总整编.doc

c语言学习知识编程经典编辑题汇总整编.doc - C语言编程基础习题汇总1.设计一个由键盘输入三个整数&#xff0c;求这三个数的平均数的程序。2.编写一个程序&#xff0c;用于水果店售货员结账。已知苹果每斤2.50元&#xff0c;鸭梨每斤1.80元&#xff0c;香蕉每斤2元&#xff0c;…

cp 过程中目录突然挂了_怎么解决管材激光切割机切管过程中出现的过烧及挂渣...

近年来&#xff0c;随着激光切割机的发展&#xff0c;不仅被用于切割平面板材&#xff0c;而且被应用于切割管材&#xff0c;我们称之为“管材激光切割机”。那么管材激光切割机用于切割管材时是否会出现问题呢&#xff0c;是的&#xff0c;确实出现了问题。由于管材的封闭性&a…

识别产品外观的合格软件_你还在犹豫?外观检测设备使用已成主流!

今天跟大伙聊一聊关于各种瓶子的外观检测/视觉检测。我们都知道&#xff0c;不管是瓶子、杯子还是其他的&#xff0c;在生产过程种对产品的质量检测这种有着超高重复性动作与随机应变能力的工作&#xff0c;在以前的工业时代&#xff0c;完全依靠人“人眼识别”人工检测来完成&…