使用存储在大型XML文件(PHP)中的二叉树林

时间:2015-03-24 01:23:01

标签: php xml parsing xml-parsing xmlreader

我有一个类似'var1'=>1.05, 'var2'=>0.76,...的数组和一个存储在100多MB XML文件中的二叉树林。

<Tree id="1">
<Node id="2">
   <SimplePredicate field="var1" operator="lessOrEqual" value="1.41"/>
   <Node id="4">
     <SimplePredicate field="var2" operator="lessOrEqual" value="1.43"/>
     .......
     </Node>
</Node>
<Node id="3">
   <SimplePredicate field="var1" operator="greaterThan" value="1.41"/>
   .......
</Node>
</Tree>

我想在PHP中做的是为每个树存储一个叶子的属性,我将根据每个节点给出的条件来结束它。因此,在此示例中,路径将是(2) - &gt;(4) - &gt; ...

由于文件大小很明显,XMLReader是阅读每棵树的正确工具。因为树很小,所以可以在处理树时将它们存储到内存中。 什么是最直接的树木工作方式?

1 个答案:

答案 0 :(得分:3)

您正在使用XMLReader进入正确的轨道。相当方便,它包括方法expand(),它将当前节点的副本作为DOMNode返回。这将允许您使用DOM API处理内存中的每个树。

至于处理节点 - 递归地评估和下降。


实施例

$data = [
    'var1' => 1.05,
    'var2' => 0.76
];

$dom    = new DOMDocument();
$xpath  = new DOMXPath($dom);
$reader = new XMLReader();
$reader->open('forest.xml');

// Read until reaching the first Tree.
while ($reader->read() && $reader->localName !== 'Tree');

while ($reader->localName === 'Tree') {
    $tree = $dom->importNode($reader->expand(), true);

    echo evaluateTree($data, $tree, $xpath), "\n";

    // Move on to the next.
    $reader->next('Tree');
}

$reader->close();

function evaluateTree(array $data, DOMElement $tree, DOMXPath $xpath)
{
    foreach ($xpath->query('./Node', $tree) as $node) {
        $field    = $xpath->evaluate('string(./SimplePredicate/@field)', $node);
        $operator = $xpath->evaluate('string(./SimplePredicate/@operator)', $node);
        $value    = $xpath->evaluate('string(./SimplePredicate/@value)', $node);

        if (evaluatePredicate($data[$field], $operator, $value)) {
            // Descend recursively.
            return evaluateTree($data, $node, $xpath);
        }
    }

    // Reached the end of the line.
    return $tree->getAttribute('id');
}

function evaluatePredicate($left, $operator, $right)
{
    switch ($operator) {
        case "lessOrEqual":
            return $left <= $right;
        case "greaterThan":
            return $left > $right;
        default:
            return false;
    }
}

输出:

4