2012-03-19 4 views
1

文字列を正規表現で分割してから一致する部分を見つけたところでdom要素を作成し、文字列が終わるまで行います。 文字列が指定されています。部分文字列が見つかった場合は、DOM要素を作成します-php

$str="hi there! [1], how are you? [2]"; 

望ましい結果:

<sentence> 
hi there! <child1>1</child1>, how are you? <child2>2</child2> 
</sentence> 

私はルートを作成するためにphp dom -> $dom = new DOMDocument('1.0'); ...

を使用しています。私のようないくつかのアプローチを使用し、

 $root= $dom->createElement('sentence', null); 
     $root= $dom->appendChild($root); 
     $root->setAttribute('attr-1', 'value-1'); 

(..これは何の関係も持っていないかもしれませんが、一部の人々が無努力とか文句を言う)preg-splitといくつか。

$counter=1; 
$pos = preg_match('/\[([1-9][0-9]*)\]/', $str); 
    if ($pos == true) { 
    $substr=$dom->createElement('child', $counter); 
    $root->appendChild($substr); 
    $counter++; 
    } 

私は

答えて

3

あなたの元のコードは、そこまでのではありません。しかし、追加したいテキストに正規表現を一致させる必要があります(そしてそのためにtextnodeが必要です)。各試合の後、あなたは同様に、一致し続けるためにどこオフセット推進する必要があります。

$str = "hi there! [1], how are you? [2]"; 

$dom = new DOMDocument('1.0'); 
$root= $dom->createElement('sentence', null); 
$root= $dom->appendChild($root); 
$root->setAttribute('attr-1', 'value-1'); # ... 

$counter = 0; 
$offset = 0; 
while ($pos = preg_match('/(.*?)\[([1-9][0-9]*)\]/', $str, $matches, NULL, $offset)) { 
    list(, $text, $number) = $matches; 
    if (strlen($text)) { 
     $root->appendChild($dom->createTextNode($text)); 
    } 
    if (strlen($number)) { 
     $counter++; 
     $root->appendChild($dom->createElement("child$counter", $number)); 

    } 
    $offset += strlen($matches[0]); 
} 

whileループに匹敵するifあなただけのループにそれを回す、持っていました。またtextnodesがマッチしたいくつかのテキストがある場合に追加されます(あなたが持っている可能性が例えば、[1] [2]あなたの文字列内のテキストが空になるように、この例の出力:。

<?xml version="1.0"?> 
<sentence attr-1="value-1"> 
    hi there! <child1>1</child1>, how are you? <child2>2</child2> 
</sentence> 

編集これをちょっと試してみたら、問題を分けたいと思うかもしれないという結論に至りました。もう一つは文字列を解析することです。もう一つは実際にノードを挿入することです(テキストの場合はtextnode、数字の場合はelementnode)。後ろから、これはすぐに実用的に見える、第2部が最初になる:

$dom = new DOMDocument('1.0'); 
$root = $dom->createElement('sentence', null); 
$root = $dom->appendChild($root); 
$root->setAttribute('attr-1', 'value-1'); # ... 

$str = "hi there! [1], how are you? [2] test"; 

$it = new Tokenizer($str); 
$counter = 0; 
foreach ($it as $type => $string) { 
    switch ($type) { 
     case Tokenizer::TEXT: 
      $root->appendChild($dom->createTextNode($string)); 
      break; 

     case Tokenizer::NUMBER: 
      $counter++; 
      $root->appendChild($dom->createElement("child$counter", $string)); 
      break; 

     default: 
      throw new Exception(sprintf('Invalid type %s.', $type)); 
    } 
} 

echo $dom->saveXML(); 

この例では、解析はまったく気にしません。テキストまたは数字($type)を取得し、textnodeまたは要素を挿入するかどうかを決定することができます。だから、文字列の解析が行われますが、このコードは常に動作します。問題がある場合(たとえば、$counterはもう面白くない)、文字列の解析/トークン化とは関係ありません。

構文解析自体は、Tokenizerと呼ばれるIteratorにカプセル化されています。それは、文字列と数字の要素に分割するためのすべてを含んでいます。それはそうでありいくつかのテキストは、最後の番号の後にあるとどうなるかのようなすべての詳細を扱って:

class Tokenizer implements Iterator 
{ 
    const TEXT = 1; 
    const NUMBER = 2; 
    private $offset; 
    private $string; 
    private $fetched; 

    public function __construct($string) 
    { 
     $this->string = $string; 
    } 

    public function rewind() 
    { 
     $this->offset = 0; 
     $this->fetch(); 
    } 

    private function fetch() 
    { 
     if ($this->offset >= strlen($this->string)) { 
      return; 
     } 
     $result = preg_match('/\[([1-9][0-9]*)\]/', $this->string, $matches, PREG_OFFSET_CAPTURE, $this->offset); 
     if (!$result) { 
      $this->fetched[] = array(self::TEXT, substr($this->string, $this->offset)); 
      $this->offset = strlen($this->string); 
      return; 
     } 
     $pos = $matches[0][1]; 
     if ($pos != $this->offset) { 
      $this->fetched[] = array(self::TEXT, substr($this->string, $this->offset, $pos - $this->offset)); 
     } 
     $this->fetched[] = array(self::NUMBER, $matches[1][0]); 
     $this->offset = $pos + strlen($matches[0][0]); 
    } 

    public function current() 
    { 
     list(, $current) = current($this->fetched); 
     return $current; 
    } 

    public function key() 
    { 
     list($key) = current($this->fetched); 
     return $key; 
    } 

    public function next() 
    { 
     array_shift($this->fetched); 
     if (!$this->fetched) $this->fetch(); 
    } 

    public function valid() 
    { 
     return (bool)$this->fetched; 
    } 
} 

行わそれは互いに離れた二つの問題を分割しています。イテレータ・クラスの代わりに、配列の配列などを作成することも可能ですが、イテレータがより有用であることがわかりましたので、すぐに書きました。

この例でもXMLが最後に出力されるため、ここでは例です。最後の要素の後にテキストを追加したことに注意してください。

<?xml version="1.0"?> 
<sentence attr-1="value-1"> 
    hi there! <child1>1</child1>, how are you? <child2>2</child2> test 
</sentence> 
+0

ありがとうhakre、私は今あなたのソリューションを実装しようとしています。 – teutara

+0

私はまだ何かが不足しています:(投稿します。 – teutara

+0

@テュララ:それは非常に安定していない、私は助けになるかもしれない答えを編集しました:問題を分割してください。 – hakre

-1

は、その後の解析、最初の正規表現との交換を行います。..任意のヘルプは高く評価され

...そのコードは価値がないですけど、もうそれは治療ではありません表示するようにドキュメント。

$xml = preg_replace('/\[(\d+)\]/', '<child$1>$1</child$1>', $str); 
$doc = new DOMDocument('1.0'); 
$doc->loadXML("<sentence>$xml</sentence>"); 

Here's a demo.

+0

ありがとうございますが、後で作成されたxmlを解析するので、これは役に立たないでしょう。私はを得ることができません。 – teutara

+0

@teutara:すみません、あなたはそれを言い換えることができますか? – Ryan

+0

データがdbから来て、同じレベルや上などでたくさんの要素が作成されているため、子が文字列として 'help'を得ていることを意味します。 – teutara

関連する問題