2013-03-21 18 views
9

<p>タグ内にラップする必要があるすべてのルートレベル#textノード(またはdivの親を持つノード)を探したいと思います。次のテキストには、最終的なルートの3つ(またはわずか2つ)の<p>タグがあります。#textノードを見つけて段落タグを折り返すDOM xpath

考えられるのは、テキストブロックをHTML表示用の段落にグループ化するように、テキストを整形することです。しかし、私が取り組んできた次のxpathは、テキストノードを選択することができないようです。

<?php 

$html = '<div> 
    This text should be wrapped in a p tag. 
</div> 

This also should be wrapped. 

<b>And</b> this.'; 

libxml_use_internal_errors(TRUE); 

$dom = DOMDocument::loadHTML($html); 

$xp = new DOMXPath($dom); 

$xpath = '//text()[not(parent::p) and normalize-space()]'; 

foreach($xp->query($xpath) as $node) { 
    $element = $dom->createElement('p'); 
    $node->parentNode->replaceChild($element, $node); 
    $element->appendChild($node); 
} 

print $dom->saveHTML(); 
+3

なぜ 'div'の外のテキストノードも選択したいのであれば、XPath式に' // div'を入れましたか? [このフィドル](http://codepad.org/hzOefCsH)はあなたが望むことをするようだ。 – nwellnhof

+0

私が上に投稿した解決策に何が間違っているか教えていただけますか?改行を含むテキストを複数の段落に変換したいですか? – nwellnhof

+0

@nwellnhof、あなたの解決策は申し訳ありませんが、それは答えではありません。 – Xeoncross

答えて

5

[OK]ですので、私の答えを答えて言います。すべてのテキストノードを一致させる場合は、XPath式から//div部分を削除するだけです。だから、次のようになります。あなたが望むなら

//text()[not(parent::p) and normalize-space()] 
+0

これはどのように ''

this text is ok
で動作するのですか?については、? ''? – mzedeler

1

あなたは純粋なJavaScriptでそれを行うことができます。

var content = document.evaluate(
             '//text()', 
             document, 
             null, 
             XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, 
             null); 

for (var i=0 ; i < content .snapshotLength; i++){ 
    console.log(content .snapshotItem(i).textContent); 
} 
2

あなたのシナリオでは、多くのエッジケースとが上に追加されなければならない単語があります。私はあなたが古典をしたいと思うが、ダブルブレイクは、新しい段落のthingyを開始しますが、今度は親<div>(または確かに他のブロック要素)内でも同様です。

私は仕事のほとんどをHTMLパーサーに任せましたが、私はまだテキスト検索を行い、(xpathの次に)置き換えます。だから、あなたが来るのはちょっとハッキリですが、私はかなり安定していると思います。

まず、上記のdivのトップレベルまたは子のすべてのテキストノードを選択します。

(.|./div)/text() 

このXPathはDOMDocumentにロードされたとき、それはあなたのHTMLフラグメントのルートタグを表して<body>タグであるアンカー要素に相対的です。

divの子の場合は、最初の段落を最初に挿入します。

次に、新しい段落を開始するシーケンスの各出現箇所にブレークマーク(ここではコメントの形式)を挿入します(空白の正規化のために"\n\n"にする必要があります。これは当てはまりません。この作業を透過的に行うには、空白の正規化を先に行う必要があります)。

/* @var $result DOMText[] */ 
$result = $xp->query('(.|./div)/text()', $anchor); 

foreach ($result as $i => $node) 
{ 
    if ($node->parentNode->tagName == 'div') 
    { 
     $insertBreakMarkBefore($node, true); 
    } 

    while (FALSE !== $pos = strpos($node->data, $paragraphSequence)) 
    { 
     $node = $node->splitText($pos + $paragraphSequenceLength); 
     $insertBreakMarkBefore($node); 
    } 
} 

これらのブレークマークは、HTML <p>タグに置き換えられます。 HTMLパーサーはそれらを適切な<p>...</p>ペアに変換します。そのため、私はこのアルゴリズムを書くことができます(これは興味深いかもしれませんが)。これは基本的に私は一度、いくつかの他の回答に概説のように動作しますが、私はちょうどリンクもはや見つけることはありません:、DOMツリーの変更後

  1. を再び<body>のinnterのHTMLを取得します。
  2. "<p>"とセットマークを交換してください(ここで私はこれが見えるようにするだけでなく、クラスをマーク)
  3. 適切<p>...</p>ペアと再作成DOMに再びパーサにHTMLフラグメントをロードします。
  4. 最後に、DOMDocumentパーサからHTMLを再度取得します。

コードでこれらの手順(モーメントの関数定義の一部をスキップする):この示すように

$needle = sprintf('%1$s<!--%2$s-->%1$s', $paragraphSequence, $paragraphComment); 
$replace = sprintf("\n<p class=\"%s\">\n", $paragraphComment); 
$html = strtr($innerHTML($anchor), array($needle . $needle => $replace, $needle => $replace)); 

echo "HTML afterwards:\n", $innerHTML($loadHTMLFragment($html)); 

、ダブル配列は、単一のものと交換されています。おそらく末尾の1つも削除する必要があります(もしapplicaleなら、ここで空白を整えることもできます)。

最終的なHTML出力:素敵な出力フォーマットのため

<div> 
<p class="break"> 

    This text should be wrapped in a p tag. 
</p> 
</div> 
<p class="break"> 
This also should be wrapped. 
</p> 
<p class="break"> 
<b>And</b> this.</p> 

いくつかのより多くのポストプロダクションも便利です。実際には、アルゴリズムを調整するのに役立つと思うのですが(Full Demo - 空白の正規化はおそらくそこには適用されないので注意して使用してください)。

+0

しかし、あなたのデモは実際にこの結果を達成していないようです。 – Xeoncross

+1

最後に書かれているように、これは空白の正規化がないためです。入力が '\ n'を行区切り文字として使用していることを確認すると、コードパッドは' \ r \ n'かsimmilarを使用しているように見えます。私は後でそれを再確認することができます、私はちょうど私がそこにそれを貼り付けたとき気づいた、自分のボックスにコードが動作します。だからあなた自身のために試してみてください。すでにあなたの箱で動作しているかもしれません。 – hakre

1

私はそれは、XPathではありません知っているが、これをチェックアウト:

PHPシンプルなHTML DOMパーサ

http://simplehtmldom.sourceforge.net/

特長

PHP5 +で書かれたHTMLのDOMパーサを使用して、操作してみましょう非常に簡単な方法でHTML!

無効なHTMLをサポートしています。

jQueryのようなセレクタでHTMLページのタグを検索します。

HTMLからコンテンツを1行に抽出します。

+0

答えはそのライブラリでどのように動作するかを示してください。そうでなければこれは部分的にしか関連していません(私の個人的な好みのために、ライブラリは悪い提案です、PHPは最近、ライブラリが提供するものをすべて内蔵しています。 DOM Parser *ライブラリはもともとPHP 4のためにlibxmlベースの拡張機能がなく、これまで存在しなかったものとして書かれていました。 – hakre

関連する問題