1

文字列を正規表現で分割し、一致するものが見つかった場所に dom 要素を作成し、文字列が終わるまでそれを行います。与えられた文字列;

$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++;
    }

私はコードが価値がないことを知っていますが、それが御馳走ではないことを示すために..

どんな助けでも大歓迎です..

4

2 に答える 2

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ループに変えただけです。また、一致するテキストがある場合は、テキストノードが追加されます (たとえば、文字列に [1][2] を含めることができるため、テキストは空になります。この例の出力:

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

編集これで少し遊んだ後、問題を分割したいという結論に達しました。1 つは文字列を解析することで、もう 1 つは実際にノードを挿入することです (たとえば、テキストの場合は 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) を取得し、テキストノードまたは要素のどちらを挿入するかを決定できます。したがって、文字列の解析がどのように行われても、このコードは常に機能します。それに問題がある場合 (たとえば、$counterもはや面白くない)、文字列の解析/トークン化とは関係ありません。

Iterator解析自体は、呼び出された にカプセル化されていますTokenizer。文字列をテキスト要素と数値要素に分割するためのすべてが含まれています。最後の数字の後にテキストがある場合にどうなるかなど、すべての詳細を処理します。

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;
    }
}

これにより、2 つの問題が互いに分離されました。イテレータ クラスの代わりに、配列の配列などを作成することもできますが、イテレータの方が便利であることがわかったので、すぐに作成しました。

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

<?xml version="1.0"?>
<sentence attr-1="value-1">
  hi there! <child1>1</child1>, how are you? <child2>2</child2> test
</sentence>
于 2012-03-19T15:44:16.307 に答える
-1

最初に正規表現による置換を実行してから、ドキュメントを解析します。

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

これがデモです。

于 2012-03-19T15:29:16.470 に答える