我在问题 PHP simplexml xpath search for value in an ELEMENT containing tab delimited text? 中给出了一个广泛的示例,它展示了如何即时操作包含编码数据的 SimpleXMLElement,将其转换为更易于访问的数据。
在您的情况下,这可以类似地完成,但是这次我想展示如何使用自定义迭代器来完成。
但首先让我们考虑如何表示一个表:一个表由行和列组成。所以我们可以迭代行,每一行都可以是列的迭代器。
关于您的问题,表上还有另一个迭代器。所以在伪代码中,这可能看起来像:
foreach($tables as $table)
{
foreach ($table as $rows) {
foreach ($rows as $column) {
}
}
}
在这附近的某个地方进行输出。因此,例如,如果我们有一个迭代器,它能够迭代行和列以将其转换为表(例如到目前为止用于演示目的的纯文本),伪代码可以简化为:
foreach($tables as $table)
{
echo new TextTable($table);
}
正如本例所示,问题已经简化为显示单个表格 - 因为所有表格都遵循相同的结构。我现在不显示TextTable 的作品,它没有进一步的兴趣。如果您正在寻找在同一迭代之后输出 HTML 表的迭代器,请参阅上面的链接问题,它有一个 HtmlTableIterator 可以完成大部分工作。但是现在,让我们看看如何在行和列上创建迭代器。
为此,我向您介绍一个名为DecoratingIterator 的现有迭代器类。它允许在迭代中装饰每个 current 元素。这非常灵活,因为我们可以使用它以非常灵活的方式分小步组合最终的表迭代器。
让我们一步一步解决您面临的所有问题。首先需要一个函数将数字 0 转换为空字符串。让我们为此创建一个函数(如果需要,我还添加了更多格式,通过在前面加上空格来获得长度为 2):
$formatNumber = function($number)
{
if ($number === '0') {
$number = '';
}
return sprintf("%' 2s", $number);
};
下一个要解决的问题是将SimpleXMLElement 的数字转换为可用于格式化的数字。让我们也编写它并使用之前的函数来帮助完成它:
$numbersToArray = function($stringNumbers) use ($formatNumber) {
return array_map($formatNumber, explode('-', $stringNumbers));
};
哇,太短了。下一个要解决的问题是将单个表的所有行转换为这些数字数组。好吧,再一次,我们在最后一个函数的帮助下做到了:
$tableToRows = function(SimpleXMLElement $table) use ($numbersToArray) {
return new DecoratingIterator($table->children(), $numbersToArray);
};
最后还有一个问题:需要将整个 XML 转换为所有表的迭代器。再次使用之前的函数,这再次变得更加简单:
$tables = new DecoratingIterator(
$xml->message->ticketpage->ticket,
$tableToRows
);
哇。可能有点长,我们回顾一下:
$formatNumber = function($number)
{
if ($number === '0') {
$number = '';
}
return sprintf("%' 2s", $number);
};
$numbersToArray = function(SimpleXMLElement $stringNumbers) use ($formatNumber) {
return array_map($formatNumber, explode('-', $stringNumbers));
};
$tableToRows = function(SimpleXMLElement $table) use ($numbersToArray) {
return new DecoratingIterator($table->children(), $numbersToArray);
};
$tables = new DecoratingIterator(
$xml->message->ticketpage->ticket,
$tableToRows
);
所有这些代码行的作用是提供$tables 迭代器,我们可以使用它来显示表格。所以为了让它更易于使用,我们将它包装到它自己的一个类中并且告诉 PHP 这个类可以聚合一个通过IteratorAggregate 接口工作的迭代器:
class TableAggregator implements IteratorAggregate
{
private $xml;
public function __construct(SimpleXMLElement $xml)
{
$this->xml = $xml;
}
public function getIterator()
{
# ... code to create the tables iterator
return $tables;
}
}
好的,到目前为止可能已经很多了。现在更好地展示用法示例,以使 why 聚合在这里有意义:
$xml = simplexml_load_file('example.xml');
$tables = new TableAggregator($xml);
foreach ($tables as $table) {
echo new TextTable($table), "\n";
}
正如本例所示,这是因为它易于使用。此外,如果我们有不同的格式化需求,我们可以创建一个不同的aggregator - 就是这么简单。让我们看看示例输出:
+--+--+--+--+--+--+--+--+--+
| |10|27|30|45| | | |80|
+--+--+--+--+--+--+--+--+--+
| |15| |38| |51|62| |85|
+--+--+--+--+--+--+--+--+--+
| 5| | |37| |57|60|77| |
+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+
| | |20|33| |56|68| |90|
+--+--+--+--+--+--+--+--+--+
| 8| | | |49| |64|71|84|
+--+--+--+--+--+--+--+--+--+
| 1|18|22|32| |59| | | |
+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+
| | |23| |47|58|67| |86|
+--+--+--+--+--+--+--+--+--+
| 4|16| | |43|53| | |88|
+--+--+--+--+--+--+--+--+--+
| 3| |28|35| | |65|72| |
+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+
| |19|26| |48|52| |74| |
+--+--+--+--+--+--+--+--+--+
| | |21| |40| |63|75|82|
+--+--+--+--+--+--+--+--+--+
| 9|11| |34|41| | |76| |
+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+
| |12|29|36|44| | |78| |
+--+--+--+--+--+--+--+--+--+
| 6|14| |39| | |69| |89|
+--+--+--+--+--+--+--+--+--+
| 2| | | | |54|66|70|81|
+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+
| |17|25| |46| | |73|87|
+--+--+--+--+--+--+--+--+--+
| | |24| |42|50|61|79| |
+--+--+--+--+--+--+--+--+--+
| 7|13| |31| |55| | |83|
+--+--+--+--+--+--+--+--+--+
整个例子一目了然:https://gist.github.com/hakre/5734770