用 sax 解析器读取 xml
读取 xml 的另一种方法是使用 xml simple api(sax)解析器。php 的大多数安装都包含 sax 解析器。sax 解析器运行在回调模型上。每次打开或关闭一个标记时,或者每次解析器看到文本时,就用节点或文本的信息回调用户定义的函数。
sax 解析器的优点是,它是真正轻量级的。解析器不会在内存中长期保持内容,所以可以用于非常巨大的文件。缺点是编写 sax 解析器回调是件非常麻烦的事。清单 3 显示了使用 sax 读取图书 xml 文件并显示内容的代码。
清单 3. 用 sax 解析器读取图书 xml
<?php
$g_books = array();
$g_elem = null;
function startelement( $parser, $name, $attrs )
{
global $g_books, $g_elem;
if ( $name == 'book' ) $g_books []= array();
$g_elem = $name;
}
function endelement( $parser, $name )
{
global $g_elem;
$g_elem = null;
}
function textdata( $parser, $text )
{
global $g_books, $g_elem;
if ( $g_elem == 'author'
$g_elem == 'publisher'
$g_elem == 'title' )
{
$g_books[ count( $g_books ) - 1 ][ $g_elem ] = $text;
}
}
$parser = xml_parser_create();
xml_set_element_handler( $parser, "startelement", "endelement" );
xml_set_character_data_handler( $parser, "textdata" );
$f = fopen( 'books.xml', 'r' );
while( $data = fread( $f, 4096 ) )
{
xml_parse( $parser, $data );
}
xml_parser_free( $parser );
foreach( $g_books as $book )
{
echo $book['title']." - ".$book['author']." - ";
echo $book['publisher']."\n";
}
?>
脚本首先设置 g_books 数组,它在内存中容纳所有图书和图书信息,g_elem 变量保存脚本目前正在处理的标记的名称。然后脚本定义回调函数。在这个示例中,回调函数是 startelement、endelement 和 textdata。在打开和关闭标记的时候,分别调用 startelement 和 endelement 函数。在开始和结束标记之间的文本上面,调用 textdata。
在这个示例中,startelement 标记查找 book 标记,在 book 数组中开始一个新元素。然后,textdata 函数查看当前元素,看它是不是 publisher、title 或 author 标记。如果是,函数就把当前文本放入当前图书。
为了让解析继续,脚本用 xml_parser_create 函数创建解析器。然后,设置回调句柄。之后,脚本读取文件并把文件的大块内容发送到解析器。在文件读取之后,xml_parser_free 函数删除解析器。脚本的末尾输出 g_books 数组的内容。
可以看到,这比编写 dom 的同样功能要困难得多。如果没有 dom 库也没有 sax 库该怎么办?还有替代方案么?
用正则表达式解析 xml
可以肯定,即使提到这个方法,有些工程师也会批评我,但是确实可以用正则表达式解析 xml。清单 4 显示了使用 preg_ 函数读取图书文件的示例。
清单 4. 用正则表达式读取 xml
<?php
$xml = "";
$f = fopen( 'books.xml', 'r' );
while( $data = fread( $f, 4096 ) ) { $xml .= $data; }
fclose( $f );
preg_match_all( "/\<book\>(.*?)\<\/book\>/s",
$xml, $bookblocks );
foreach( $bookblocks[1] as $block )
{
preg_match_all( "/\<author\>(.*?)\<\/author\>/",
$block, $author );
preg_match_all( "/\<title\>(.*?)\<\/title\>/",
$block, $title );
preg_match_all( "/\<publisher\>(.*?)\<\/publisher\>/",
$block, $publisher );
echo( $title[1][0]." - ".$author[1][0]." - ".
$publisher[1][0]."\n" );
}
?>
请注意这个代码有多短。开始时,它把文件读进一个大的字符串。然后用一个 regex 函数读取每个图书项目。最后用 foreach 循环,在每个图书块间循环,并提取出 author、title 和 publisher。
那么,缺陷在哪呢?使用正则表达式代码读取 xml 的问题是,它并没先进行检查,确保 xml 的格式良好。这意味着在读取之前,无法知道 xml 是否格式良好。而且,有些格式正确的 xml 可能与正则表达式不匹配,所以日后必须修改它们。
我从不建议使用正则表达式读取 xml,但是有时它是兼容性最好的方式,因为正则表达式函数总是可用的。不要用正则表达式读取直接来自用户的 xml,因为无法控制这类 xml 的格式或结构。应当一直用 dom 库或 sax 解析器读取来自用户的 xml。
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器