仓酷云

标题: PHP教程之PHP的XML剖析函数 [打印本页]

作者: 精灵巫婆    时间: 2015-2-4 00:26
标题: PHP教程之PHP的XML剖析函数
到现在,对排版还是不很熟练,经常会排不好。xml|函数   起首我得供认我喜好盘算机尺度。假如每一个人都服从这个行业的尺度,互联网将会是一个更好的媒体。利用尺度化的数据互换格局才干使开放的和自力于平台的盘算形式实在可行。这就是我作为XML喜好者的缘由。

侥幸的是,我最喜欢的剧本言语不仅撑持XML并且对其撑持正不休增强。PHP可让我敏捷将XML文档宣布到互联网上,搜集XML文档的统计信息,将XML文档转换成其它格局。例如,我经常用PHP的XML处置才能来办理我用XML所写的文章和书。

本文中,我将会商任何用PHP内建的Expat解析器来处置XML文档。经由过程典范,我将演示Expat的处置办法。同时,典范可以告知你若何:

创立你本人的处置函数
将XML文档转换成你本人的PHP数据布局

引见Expat

XML的解析器,一样称为XML处置器,可使法式会见XML文档的布局和内容。Expat是PHP剧本言语的XML解析器。它同时也应用在其它项目中,例如Mozilla、Apache和Perl。

甚么是基于事务的解析器?

XML解析器的两种根基类型:

基于树型的解析器:将XML文档转换成树型布局。这类解析器剖析整篇文章,同时供应一个API来会见所发生树的每一个元素。其通用的尺度为DOM(文档对象形式)。
基于事务的解析器:将XML文档视为一系列的事务。当一个特别事务产生时,解析器将挪用开辟者供应的函数来处置。
基于事务的解析器有一个XML文档的数据集中视图,也就是说它集中在XML文档的数据局部,而不是其布局。这些解析器从头至尾处置文档,并将相似于-元素的入手下手、元素的开头、特点数据的入手下手等等-事务经由过程回调(callback)函数呈报给使用法式。以下是一个"Hello-World"的XML文档典范:

<greeting>
Hello World
</greeting>

基于事务的解析器将呈报为三个事务:

入手下手元素:greeting
CDATA项的入手下手,值为:Hello World
停止元素:greeting
不像基于树型的解析器,基于事务的解析器不发生描写文档的布局。在CDATA项中,基于事务的解析器不会让你失掉父元素greeting的信息。
但是,它供应一个更底层的会见,这就使得可以更好天时用资本和更快地会见。经由过程这类体例,就没有需要将全部文档放入内存;而现实上,全部文档乃至可以大于实践内存值。


Expat就是如许的一种基于事务的解析器。固然假如利用Expat,需要时它一样可以在PHP中生成完整的原生树布局。


下面Hello-World的典范包含完全的XML格局。但它是有效的,由于既没有DTD(文档类型界说)与其接洽,也没有内嵌DTD。


关于Expat,这并没有区分:Expat是一个不反省无效性的解析器,因而疏忽任何与文档接洽的DTD。但应注重的是文档依然需求完全的格局,不然Expat(和其他合适XML尺度的解析器一样)将会跟着失足信息而中断。


作为不反省无效性的解析器,Exapt的疾速性和轻便性使其非常合适互联网法式。


编译Expat

Expat可以编译进PHP3.0.6版本(或以上)中。从Apache1.3.9入手下手,Expat已作为Apache的一局部。在Unix体系中,经由过程-with-xml选项设置装备摆设PHP,你可以将其编译入PHP。


假如你将PHP编译为Apache的模块,而Expat将默许作为Apache的一局部。在Windows中,你则必需要加载XML静态毗连库。

XML典范:XMLstats

懂得Expat的函数的一个举措就是经由过程典范。咱们所要会商的典范是利用Expat来搜集XML文档的统计数据。


关于文档中每一个元素,以下信息都将被输入:

该元素在文档中利用的次数
该元素中字符数据的数目
元素的父元素
元素的子元素
注重:为了演示,咱们使用PHP来发生一个布局来保留元素的父元素和子元素

筹办

用于发生XML解析器实例的函数为xml_parser_create()。该实例将用于今后的一切函数。这个思绪十分相似于PHP中MySQL函数的毗连标志。在解析文档前,基于事务的解析器凡是请求你注册回调函数-用于特定的事务产生时挪用。Expat没有破例事务,它界说了以下七个能够事务:


对象 XML解析函数 描写

元素 xml_set_element_handler() 元素的入手下手和停止

字符数据 xml_set_character_data_handler() 字符数据的入手下手

内部实体 xml_set_external_entity_ref_handler() 内部实体呈现

未解析内部实体 xml_set_unparsed_entity_decl_handler() 未解析的内部实体呈现

处置指令 xml_set_processing_instruction_handler() 处置指令的呈现

记法声明 xml_set_notation_decl_handler() 记法声明的呈现

默许 xml_set_default_handler() 其它没有指定处置函数的事务

一切的回调函数必需将解析器的实例作为其第一个参数(另外还有其它参数)。


关于本文最初的典范剧本。你需求注重的是它既用到了元素处置函数又用到了字符数据处置函数。元素的回调解理函数经由过程xml_set_element_handler()来注册。


这个函数需求三个参数:

解析器的实例
处置入手下手元素的回调函数的称号
处置停止元素的回调函数的称号
当入手下手解析XML文档时,回调函数必需存在。它们必需界说为与PHP手册中所描写的原型分歧。


例如,Expat将三个参数传递给入手下手元素的处置函数。在剧本典范中,其界说以下:


function start_element($parser, $name, $attrs)


第一个参数是解析器标示,第二个参数是入手下手元素的称号,第三参数为包括元素一切属性和值的数组。


一旦你入手下手解析XML文档,Expat在碰到入手下手元素是都将挪用你的start_element()函数并将参数传递曩昔。


XML的Case Folding选项

用xml_parser_set_option()函数将Case folding选项封闭。这个选项默许是翻开的,使得传递给处置函数的元素名主动转换为大写。但XML对巨细写是敏感的(所以巨细写对统计XML文档长短常主要的)。关于咱们的典范,case folding选项必需封闭。


解析文档

在完成一切的筹办任务后,如今剧本终究可以解析XML文档:

Xml_parse_from_file(),一个自界说的函数,翻开参数中指定的文件,并以4kb的巨细停止解析
xml_parse()和xml_parse_from_file()一样,当产生毛病时,即XML文档的格局不完整时,将会前往false。
你可使用xml_get_error_code()函数来失掉最初一个毛病的数字代码。将此数字代码传递给xml_error_string()函数便可失掉毛病的文本信息。
输入XML以后的行数,使得调试更轻易。
在解析的过程当中,挪用回调函数。
描写文档布局

当解析文档时,关于Expat需求强调成绩的是:若何坚持文档布局的根基描写?


如前所述,基于事务的解析器自己其实不发生任何布局信息。


不外标签(tag)布局是XML的主要特征。例如,元素序列<book><title>暗示的意思分歧于<figure><title>。也就是说,任何作者城市告知你书名和图名是没有关系的,固然它们都用到"title"这个术语。因而,为了更无效地利用基于事务的解析器处置XML,你必需利用本人的栈(stacks)或列表(lists)来保护文档的布局信息。


为了发生文档布局的镜像,剧本最少需求晓得今朝元素的父元素。用Exapt的API是没法完成的,它只呈报今朝元素的事务,而没有任何前后关系的信息。因而,你需求创立本人的栈布局。


剧本典范利用先辈后出(FILO)的栈布局。经由过程一个数组,栈将保留全体的入手下手元素。关于入手下手元素处置函数,今朝的元素将被array_push()函数推到栈的顶部。响应的,停止元素处置函数经由过程array_pop()将最顶的元素移走。


关于序列<book><title></title></book>,栈的填充以下:

入手下手元素book:将"book"赋给栈的第一个元素($stack[0])。
入手下手元素title:将"title"赋给栈的顶部($stack[1])。
停止元素title:从栈中将最顶部的元素移去($stack[1])。
停止元素title:从栈中将最顶部的元素移去($stack[0])。
PHP3.0经由过程一个$depth变量手动掌握元素的嵌套来完成典范。这就使剧本看起来对照庞杂。PHP4.0经由过程array_pop()和array_push()两个函数来使剧本看起来更简约。


搜集数据

为了搜集每一个元素的信息,剧本需求记住每一个元素的事务。经由过程利用一个全局的数组变量$elements来保留文档中一切分歧的元素。数组的项目是元素类的实例,有4个属性(类的变量)

$count -该元素在文档中被发明的次数
$chars -元素中字符事务的字节数
$parents -父元素
$childs - 子元素
正如你所看到的,将类实例保留在数组中是垂手可得的。


注重:PHP的一个特征是你可以经由过程while(list() = each())loop遍历全部类布局,好像你遍历全部响应的数组一样。一切的类变量(当你用PHP3.0时还无方法名)都以字符串的体例输入。


当发明一个元素时,咱们需求增添其响应的记数器来跟踪它在文档中呈现几何次。在响应的$elements项中的记数元素也要加一。


咱们一样要让父元素晓得今朝的元素是它的子元素。因而,今朝元素的称号将会到场到父元素的$childs数组的项目中。最初,今朝元素应当记住谁是它的父元素。所以,父元素被到场到今朝元素$parents数组的项目中。


显示统计信息

剩下的代码在$elements数组和其子数组中轮回显示其统计了局。这就是最复杂的嵌套轮回,虽然输入准确的了局,但代码既不简约又没有任何出格的技能,它仅仅是一个你能够天天用他来完成任务的轮回。


剧本典范被设计为经由过程PHP的CGI体例的号令行来挪用。因而,统计了局输入的格局为文本格局。假如你要将剧本应用到互联网上,那末你需求修正输入函数来发生HTML格局。

总结

Exapt是PHP的XML解析器。作为基于事务的解析器,它不发生文档的布局描写。但经由过程供应底层会见,这就使得可以更好天时用资本和更快地会见。


作为一个不反省无效性的解析器,Expat疏忽与XML文档毗连的DTD,但假如文档的格局不完全,它将会跟着失足信息而中断。


供应事务处置函数来处置文档
创立本人的事务布局例如栈和树来取得XML布局信息标志的长处。
天天都有新的XML法式呈现,而PHP对XML的撑持也不休增强(例如,增添了撑持基于DOM的XML解析器LibXML)。


有了PHP和Expat,你就能够为行将呈现的无效、开放和自力于平台的尺度作筹办。

典范

<?
/*****************************************************************************
* 称号:XML解析典范:XML文档信息统计
* 描写
* 本典范经由过程PHP的Expat解析器搜集和统计XML文档的信息(例如:每一个元素呈现的次数、父元素和子元素
* XML文件作为一个参数 ./xmlstats_PHP4.php3 test.xml
* $Requires: Expat 请求:Expat PHP4.0编译为CGI形式
*****************************************************************************/

// 第一个参数是XML文件
$file = $argv[1];

// 变量的初始化
$elements = $stack = array();
$total_elements = $total_chars = 0;

// 元素的根基类
class element
{
var $count = 0;
var $chars = 0;
var $parents = array();
var $childs = array();
}

// 解析XML文件的函数
function xml_parse_from_file($parser, $file)
{
if(!file_exists($file))
{
die("Can't find file "$file".");
}

if(!($fp = @fopen($file, "r")))
{
die("Can't open file "$file".");
}

while($data = fread($fp, 4096))
{
if(!xml_parse($parser, $data, feof($fp)))
{
return(false);
}
}

fclose($fp);

return(true);
}

// 输入了局函数(方框模式)
function print_box($title, $value)
{
printf("n+%'-60s+n", "");
printf("|%20s", "$title:");
printf("%14s", $value);
printf("%26s|n", "");
printf("+%'-60s+n", "");
}

// 输入了局函数(行模式)
function print_line($title, $value)
{
printf("%20s", "$title:");
printf("%15sn", $value);
}

// 排序函数
function my_sort($a, $b)
{
return(is_object($a) && is_object($b) ? $b->count - $a->count: 0);
}

function start_element($parser, $name, $attrs)
{
global $elements, $stack;

// 元素是不是已在全局$elements数组中?
if(!isset($elements[$name]))
{
// 否-增添一个元素的类实例
$element = new element;
$elements[$name] = $element;
}

// 该元素的记数器加一
$elements[$name]->count++;

// 是不是有父元素?
if(isset($stack[count($stack)-1]))
{
// 是-将父元素赋给$last_element
$last_element = $stack[count($stack)-1];

// 假如今朝元素的父元素数组为空,初始化为0
if(!isset($elements[$name]->parents[$last_element]))
{
$elements[$name]->parents[$last_element] = 0;
}

// 该元素的父元素记数器加一
$elements[$name]->parents[$last_element]++;

// 假如今朝元素的父元素的子元素数组为空,初始化为0

if(!isset($elements[$last_element]->childs[$name]))
{
$elements[$last_element]->childs[$name] = 0;
}

// 该元素的父元素的子元素记数器加一
$elements[$last_element]->childs[$name]++;
}

// 将今朝的元素到场到栈中
array_push($stack, $name);
}

function stop_element($parser, $name)
{
global $stack;

// 从栈中将最顶部的元素移去
array_pop($stack);
}

function char_data($parser, $data)
{
global $elements, $stack, $depth;

// 增添今朝元素的字符数量
$elements[$stack][count($stack)-1]]->chars += strlen(trim($data));
}

// 发生解析器的实例
$parser = xml_parser_create();

// 设置处置函数
xml_set_element_handler($parser, "start_element", "stop_element");
xml_set_character_data_handler($parser, "char_data");
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

// 解析文件
$ret = xml_parse_from_file($parser, $file);
if(!$ret)
{
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
}

// 释放解析器
xml_parser_free($parser);

// 释放协助元素
unset($elements["current_element"]);
unset($elements["last_element"]);

// 依据元素的次数排序
uasort($elements, "my_sort");

// 在$elements中轮回搜集元素信息
while(list($name, $element) = each($elements))
{
print_box("Element name", $name);

print_line("Element count", $element->count);
print_line("Character count", $element->chars);

printf("n%20sn", "* Parent elements");

// 在该元素的父中轮回,输入了局
while(list($key, $value) = each($element->parents))
{
print_line($key, $value);
}
if(count($element->parents) == 0)
{
printf("%35sn", "[root element]");
}

// 在该元素的子中轮回,输入了局
printf("n%20sn", "* Child elements");
while(list($key, $value) = each($element->childs))
{
print_line($key, $value);
}
if(count($element->childs) == 0)
{
printf("%35sn", "[no childs]");
}

$total_elements += $element->count;
$total_chars += $element->chars;
}

// 终究了局
print_box("Total elements", $total_elements);
print_box("Total characters", $total_chars);
?>

  可以在书上很方便地做标记,及时记下自己的心得体会。
作者: 飘飘悠悠    时间: 2015-2-4 21:35
当留言板完成的时候,下步可以把做1个单人的blog程序,做为目标,
作者: 金色的骷髅    时间: 2015-2-6 18:45
遇到出错的时候,我经常把错误信息直接复制到 google的搜索栏,一般情况都是能搜到结果的,不过有时候会搜出来一大片英文的出来,这时候就得过滤一下,吧中文的弄出来,挨着式方法。
作者: 老尸    时间: 2015-2-18 03:41
php里的数组为空的时候是不能拿来遍历的;(这个有点低级啊,不过我刚被这个边界问题墨迹了好长一会)
作者: 爱飞    时间: 2015-2-28 00:57
php里的数组为空的时候是不能拿来遍历的;(这个有点低级啊,不过我刚被这个边界问题墨迹了好长一会)
作者: 仓酷云    时间: 2015-3-9 17:20
其实没啥难的,多练习,练习写程序,真正的实践比看100遍都有用。不过要熟悉引擎
作者: 小女巫    时间: 2015-3-17 00:09
在我安装pear包的时候老是提示,缺少某某文件,才发现 那群extension 的排列是应该有一点的顺序,而我安装的版本的排序不是正常的排序。没办法我只好把那群冒号加了上去,只留下我需要使用的扩展。
作者: 若相依    时间: 2015-3-17 10:11
因为blog这样的可以让你接触更多要学的知识,可以接触用到类,模板,js ,ajax
作者: 深爱那片海    时间: 2015-3-24 00:22
兴趣是最好的老师,百度是最好的词典。
作者: 再现理想    时间: 2015-4-17 22:51
做为1门年轻的语言,php一直很努力。
作者: 飘灵儿    时间: 2015-4-18 11:54
作为一个合格的coder 编码的规范是必须,命名方面我推崇“驼峰法”,另外就是自己写的代码最好要带注释,不然时间长了,就算是自己的代码估计看起来都费事,更不用说别人拉。
作者: 兰色精灵    时间: 2015-4-23 06:39
我还是强烈建议自己搭建php环境。因为在搭建的过程中你会遇到一些问题,通过搜索或是看php手册解决问题后,你会更加深刻的理解它们的工作原理,了解到php配置文件中的一些选项设置。
作者: 不帅    时间: 2015-4-25 17:11
再就是混迹于论坛啦,咱们的phpchina的论坛就很强大,提出的问题一般都是有达人去解答的,以前的帖子也要多看看也能学到不少前辈们的经验。别的不错的论坛例如php100,javaeye也是很不错的。
作者: 只想知道    时间: 2015-5-5 08:50
本文当是我的笔记啦,遇到的问题随时填充
作者: 谁可相欹    时间: 2015-5-6 01:11
对于初学者来说不推荐去拿钱买的。当然如果一个网站你经常去用,而且里面的资料也比较有用,最好还是买个会员比较好,毕竟那些也是别人的工作成果。
作者: 简单生活    时间: 2015-5-6 03:11
不禁又想起那些说php是草根语言的人,为什么认得差距这么大呢。
作者: 柔情似水    时间: 2015-5-7 04:59
实践是检验自己会不会的真理。
作者: 分手快乐    时间: 2015-5-9 06:07
php里的数组为空的时候是不能拿来遍历的;(这个有点低级啊,不过我刚被这个边界问题墨迹了好长一会)
作者: 若天明    时间: 2015-6-11 03:51
要进行开发,搭建环境是首先需要做的事,windows下面我习惯把环境那个安装在C盘下面,因为我配的环境经常出现诡异事件,什么事都没做环境有的时候就不能用啦。
作者: 小魔女    时间: 2015-6-19 22:01
首推的搜索引擎当然是Google大神,其次我比较喜欢 百度知道。不过搜出来的结果往往都是 大家copy来copy去的,运气的的概率很大。




欢迎光临 仓酷云 (http://ckuyun.com/) Powered by Discuz! X3.2