0

私はこの文字列を持っています:

<ul>
  <li id="1">Page 1</li>
  <li id="2">Page 2
    <ul>
      <li id="3">Sub Page A</li>
      <li id="4">Sub Page B</li>
      <li id="5">Sub Page C
        <ul>
          <li id="6">Sub Sub Page I</li>
        </ul>
      </li>
    </ul>
  </li>
  <li id="7">Page 3
    <ul>
      <li id="8">Sub Page D</li>
    </ul>
  </li>
  <li id="9">Page 4</li>
</ul>

そして、PHPを使用してすべての情報を分解し、次のようにしたいと思います。

----------------------------------
| ID | ORDER | PARENT | CHILDREN |
----------------------------------
|  1 |   1   |   0   |     0     |
|  2 |   2   |   0   |   3,4,5   |
|  3 |   1   |   2   |     0     |
|  4 |   2   |   2   |     0     |
|  5 |   3   |   2   |     6     |
|  6 |   1   |   5   |     0     |
|  7 |   3   |   0   |     8     |
|  8 |   1   |   7   |     0     |
|  9 |   4   |   0   |     0     |
----------------------------------

追加情報については、これがこのリストの意味です。

ID 1は1番目(ページ1)であり、0人の親と0人の子があります。

ID 2は2番目(ページ2)であり、0の親と子のID 3,4,5、

ID 3は1番目(サブページA)で、親ID 2と0の子があり、

ID 4は2番目(サブページB)で、親ID 2と0の子があり、

ID 5は3番目(サブページC)で、親ID2と子ID6があります。

ID 6は1番目(サブページI)で、親ID 5と0の子があり、

ID 7は3番目(ページ3)で、ID8の親と子は0です。

ID 8は1番目(サブページI)で、親ID 7と0の子があり、

ID 9は4番目(ページ4)で、親は0、子は0です。

これが難しすぎる場合、誰かが別の方法でこの文字列からその情報を取得する方法を推測できますか?

4

3 に答える 3

2

これは「文字列」ではなく、HTMLです。DOMDocumentsimple_html_domなどのHTMLパーサーを使用する必要があります。

http://htmlparsing.com/php.htmlで例を参照してください

于 2012-12-29T17:54:42.977 に答える
1

ここで問題を分割できます。DOMDocument1 つのことは、HTML を解析することです。これは、 and DOMXpathhereで最も簡単に実行できます。これは、別の xpath 式/クエリの結果のコンテキストでマッピングを実行しています。少し複雑に聞こえるかもしれませんが、そうではありません。より単純化されたバリアントでは、 xpath とすべての子要素を介して親要素を取得する への以前の回答で概説されているこれを見つけることができます。

あなたの場合、これはもう少し複雑な疑似コードです。デモンストレーションの目的で物事をより見やすくするため、ラベルを追加しました。

foreach //li ::
    ID       := string(./@id)
    ParentID := string(./ancestor::li[1]/@id)
    Label    := normalize-space(./text()[1])

これが示すように、これは生のデータのみを返します。Order と Children もあります。通常、Children のリストは必要ありません (とにかくここに置いておきます)。Order 値と Children 値の類似点は、コンテキストから取得されることです。

たとえば、ドキュメントの順序でノードリストをトラバースしているとき//liに、各 ParentID ごとにカウンターが保持されている場合、各子の順序に番号を付けることができます。

Children と同様に、カウンターのように、リストを繰り返し処理しながら値を構築する必要があります。各 listitem の正しい値は、最後にのみ使用できます。

したがって、これら 2 つの値はコンテキスト内にあるため、ParentID: をキーとする配列の形式でそのコンテキストを作成します$parents。ID ごとに 2 つのエントリが含まれます。0 には Order のカウンタが含まれ、1 には子 (存在する場合) の ID を保持するための配列が含まれます。

注:技術的には、これは完全に正しいわけではありません。Order と Children は、純粋な xpath でも表現できるはずです。この例では、独自の非 xpath コンテキストも追加する方法を示すためにそれを行いませんでした。たとえば、別の順序付けや子の処理が必要な場合などです。

理論で十分です。標準設定を考慮すると、次のようになります。

$doc = new DOMDocument();
$doc->loadHTML($html);
$xp = new DOMXPath($doc);

上記のマッピングは含まれています。そのコンテキストは無名関数として記述できます。

$parents = [];

$map = function (DOMElement $li) use ($xp, &$parents) {

    $id       = (int)$xp->evaluate('string(./@id)', $li);
    $parentId = (int)$xp->evaluate('string(./ancestor::li[1]/@id)', $li);
    $label    = $xp->evaluate('normalize-space(./text()[1])', $li);

    isset($parents[$parentId][0]) ? $parents[$parentId][0]++ : ($parents[$parentId][0] = 1);
    $order                   = $parents[$parentId][0];
    $parents[$parentId][1][] = $id;
    isset($parents[$id][1]) || $parents[$id][1] = [];

    return array($id, $label, $order, $parentId, &$parents[$id][1]);
};

ご覧のとおり、最初に疑似コードのような値の取得が含まれ、2 番目の部分でコンテキスト値の処理が含まれます。ID / ParentID がまだ存在しない場合は、そのコンテキストを初期化するだけです。

このマッピングを適用する必要があります。

$result = [];
foreach ($xp->query('//li') as $li) {
    list($id) = $array = $map($li);
    $result[$id] = $array;
}

これには$result、アイテムのリストと$parentsコンテキスト データが含まれます。参照が使用されているため、ここで Children 値を内破する必要があります。その後、参照を削除できます。

foreach ($parents as &$parent) {
    $parent[1] = implode(',', $parent[1]);
}
unset($parent, $parents);

これにより$result、出力可能な最終結果が得られます。

echo '+----+----------------+-------+--------+----------+
| ID |     LABEL      | ORDER | PARENT | CHILDREN |
+----+----------------+-------+--------+----------+
';
foreach ($result as $line) {
    vprintf("| %' 2d | %' -14s |  %' 2d   |   %' 2d   | %-8s |\n", $line);
}
echo '+----+----------------+-------+--------+----------+
';

次に、次のようになります。

+----+----------------+-------+--------+----------+
| ID |     LABEL      | ORDER | PARENT | CHILDREN |
+----+----------------+-------+--------+----------+
|  1 | Page 1         |   1   |    0   |          |
|  2 | Page 2         |   2   |    0   | 3,4,5    |
|  3 | Sub Page A     |   1   |    2   |          |
|  4 | Sub Page B     |   2   |    2   |          |
|  5 | Sub Page C     |   3   |    2   | 6        |
|  6 | Sub Sub Page I |   1   |    5   |          |
|  7 | Page 3         |   3   |    0   | 8        |
|  8 | Sub Page D     |   1   |    7   |          |
|  9 | Page 4         |   4   |    0   |          |
+----+----------------+-------+--------+----------+

デモはオンラインでここにあります。

于 2012-12-29T19:28:15.253 に答える
0

今回は単一のマッピングでそれを行う方法を示しているため(疑似コードで)、2番目の回答を残します。

foreach //li ::
    ID       := string(./@id)
    ParentID := string(./ancestor::li[1]/@id)
    Label    := normalize-space(./text()[1])
    Order    := count(./preceding-sibling::li)+1
    Children := implode(",", ./ul/li/@id)

これは、順序に関係なく各ノードごとに実行できるため、現在の関数でliある に完全に一致する可能性があります。Iterator

public function current() {

    return [
        'ID'       => $this->evaluate('number(./@id)'),
        'label'    => $this->evaluate('normalize-space(./text()[1])'),
        'order'    => $this->evaluate('count(./preceding-sibling::li)+1'),
        'parentID' => $this->evaluate('number(concat("0", ./ancestor::li[1]/@id))'),
        'children' => $this->implodeNodes(',', './ul/li/@id'),
    ];
}

完全な例 ( Demo ) の出力とコード:

+----+----------------+-------+--------+----------+
| ID |     LABEL      | ORDER | PARENT | CHILDREN |
+----+----------------+-------+--------+----------+
|  1 | Page 1         |   1   |    0   |          |
|  2 | Page 2         |   2   |    0   | 3,4,5    |
|  3 | Sub Page A     |   1   |    2   |          |
|  4 | Sub Page B     |   2   |    2   |          |
|  5 | Sub Page C     |   3   |    2   | 6        |
|  6 | Sub Sub Page I |   1   |    5   |          |
|  7 | Page 3         |   3   |    0   | 8        |
|  8 | Sub Page D     |   1   |    7   |          |
|  9 | Page 4         |   4   |    0   |          |
+----+----------------+-------+--------+----------+


class HtmlListIterator extends IteratorIterator
{
    private $xpath;

    public function __construct($html) {

        $doc = new DOMDocument();
        $doc->loadHTML($html);
        $this->xpath = new DOMXPath($doc);
        parent::__construct($this->xpath->query('//li'));
    }

    private function evaluate($expression) {

        return $this->xpath->evaluate($expression, parent::current());
    }

    private function implodeNodes($glue, $expression) {

        return implode(
            $glue, array_map(function ($a) {

                return $a->nodeValue;
            }, iterator_to_array($this->evaluate($expression, parent::current())))
        );
    }

    public function current() {

        return [
            'ID'       => $this->evaluate('number(./@id)'),
            'label'    => $this->evaluate('normalize-space(./text()[1])'),
            'order'    => $this->evaluate('count(./preceding-sibling::li)+1'),
            'parentID' => $this->evaluate('number(concat("0", ./ancestor::li[1]/@id))'),
            'children' => $this->implodeNodes(',', './ul/li/@id'),
        ];
    }
}

print_result(new HtmlListIterator($html));

function print_result($result) {

    echo '+----+----------------+-------+--------+----------+
| ID |     LABEL      | ORDER | PARENT | CHILDREN |
+----+----------------+-------+--------+----------+
';
    foreach ($result as $line) {
        vprintf("| %' 2d | %' -14s |  %' 2d   |   %' 2d   | %-8s |\n", $line);
    }
    echo '+----+----------------+-------+--------+----------+
';
}
于 2012-12-29T21:52:47.367 に答える