PHP的XML分析函数_PHP

Jun 01, 2016 pm 12:38 PM
xml 要素 関数 分析する 書類 解析する

PHP的XML分析函数


适合的阅读对象



本文适合于打算用XML编写程序的有经验PHP程序员。本文假设你熟悉XML的语法和优点。





如果读者希望学习更多的XML知识
在阅读本文以前应浏览以下网站





What is XML? - 由Normal Walsh撰写XML介绍

XML FAQ
-XML - 常见问题

Project Cool
: XML resources - XML指南和参考

XML
.com - XML入门网站

XML
.org - XML的连接网站

Annotated XML specification
- W3C的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("%15s\n", $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%20s\n", "* Parent elements");



// 在该元素的父中循环,输出结果

while(list($key, $value) = each($element->parents))

{

print_line($key, $value);

}

if(count($element->parents) == 0)

{

printf("%35s\n", "[root element]");

}



// 在该元素的子中循环,输出结果

printf("\n%20s\n", "* Child elements");

while(list($key, $value) = each($element->childs))

{

print_line($key, $value);

}

if(count($element->childs) == 0)

{

printf("%35s\n", "[no childs]");

}



$total_elements += $element->count;

$total_chars += $element->chars;

}



// 最终结果

print_box("Total elements", $total_elements);

print_box("Total characters", $total_chars);

?>

原文为Tobias Ratschiller 所著发表在http://www.zend.com上

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

golang 関数で新しい関数を動的に作成するためのヒント golang 関数で新しい関数を動的に作成するためのヒント Apr 25, 2024 pm 02:39 PM

Go 言語は、クロージャとリフレクションという 2 つの動的関数作成テクノロジを提供します。クロージャを使用すると、クロージャ スコープ内の変数にアクセスでき、リフレクションでは FuncOf 関数を使用して新しい関数を作成できます。これらのテクノロジーは、HTTP ルーターのカスタマイズ、高度にカスタマイズ可能なシステムの実装、プラグイン可能なコンポーネントの構築に役立ちます。

C++ 関数の命名におけるパラメーターの順序に関する考慮事項 C++ 関数の命名におけるパラメーターの順序に関する考慮事項 Apr 24, 2024 pm 04:21 PM

C++ 関数の名前付けでは、読みやすさを向上させ、エラーを減らし、リファクタリングを容易にするために、パラメーターの順序を考慮することが重要です。一般的なパラメータの順序規則には、アクション-オブジェクト、オブジェクト-アクション、意味論的な意味、および標準ライブラリへの準拠が含まれます。最適な順序は、関数の目的、パラメーターの種類、潜在的な混乱、および言語規約によって異なります。

Java で効率的で保守しやすい関数を記述するにはどうすればよいでしょうか? Java で効率的で保守しやすい関数を記述するにはどうすればよいでしょうか? Apr 24, 2024 am 11:33 AM

効率的で保守しやすい Java 関数を作成するための鍵は、シンプルに保つことです。意味のある名前を付けてください。特殊な状況に対処します。適切な可視性を使用してください。

Excel関数の公式の完全なコレクション Excel関数の公式の完全なコレクション May 07, 2024 pm 12:04 PM

1. SUM 関数は、列またはセルのグループ内の数値を合計するために使用されます (例: =SUM(A1:J10))。 2. AVERAGE 関数は、列またはセルのグループ内の数値の平均を計算するために使用されます (例: =AVERAGE(A1:A10))。 3. COUNT 関数。列またはセルのグループ内の数値またはテキストの数をカウントするために使用されます。例: =COUNT(A1:A10)。 4. IF 関数。指定された条件に基づいて論理的な判断を行い、結果を返すために使用されます。対応する結果。

PHPでHTML/XMLを解析および処理するにはどうすればよいですか? PHPでHTML/XMLを解析および処理するにはどうすればよいですか? Feb 07, 2025 am 11:57 AM

このチュートリアルでは、PHPを使用してXMLドキュメントを効率的に処理する方法を示しています。 XML(拡張可能なマークアップ言語)は、人間の読みやすさとマシン解析の両方に合わせて設計された多用途のテキストベースのマークアップ言語です。一般的にデータストレージに使用されます

C++ 関数例外の詳細: カスタマイズされたエラー処理 C++ 関数例外の詳細: カスタマイズされたエラー処理 May 01, 2024 pm 06:39 PM

C++ の例外処理は、特定のエラー メッセージ、コンテキスト情報を提供し、エラーの種類に基づいてカスタム アクションを実行するカスタム例外クラスを通じて強化できます。 std::Exception から継承した例外クラスを定義して、特定のエラー情報を提供します。カスタム例外をスローするには、throw キーワードを使用します。 try-catch ブロックでdynamic_castを使用して、キャッチされた例外をカスタム例外タイプに変換します。実際の場合、open_file 関数は FileNotFoundException 例外をスローします。例外をキャッチして処理すると、より具体的なエラー メッセージが表示されます。

PHP 関数を使用して XML データを処理するにはどうすればよいですか? PHP 関数を使用して XML データを処理するにはどうすればよいですか? May 05, 2024 am 09:15 AM

PHPXML 関数を使用して XML データを処理します。 XML データを解析します。 simplexml_load_file() および simplexml_load_string() は、XML ファイルまたは文字列を読み込みます。 XML データにアクセスする: SimpleXML オブジェクトのプロパティとメソッドを使用して、要素名、属性値、およびサブ要素を取得します。 XML データを変更する: addChild() メソッドと addAttribute() メソッドを使用して、新しい要素と属性を追加します。シリアル化された XML データ: asXML() メソッドは、SimpleXML オブジェクトを XML 文字列に変換します。実用的な例: 製品フィード XML を解析し、製品情報を抽出し、変換してデータベースに保存します。

Golang関数がマップパラメータを受け取る際の注意点 Golang関数がマップパラメータを受け取る際の注意点 Jun 04, 2024 am 10:31 AM

Go の関数にマップを渡すと、デフォルトでコピーが作成され、コピーへの変更は元のマップには影響しません。元のマップを変更する必要がある場合は、ポインタを介してそれを渡すことができます。空のマップは技術的には nil ポインターであり、空ではないマップを期待する関数に空のマップを渡すとエラーが発生するため、空のマップは慎重に扱う必要があります。

See all articles