あなたの元のコードは、そこまでのではありません。しかし、追加したいテキストに正規表現を一致させる必要があります(そしてそのために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>
ありがとうhakre、私は今あなたのソリューションを実装しようとしています。 – teutara
私はまだ何かが不足しています:(投稿します。 – teutara
@テュララ:それは非常に安定していない、私は助けになるかもしれない答えを編集しました:問題を分割してください。 – hakre