PHPで置き換えられるネストされたプレースホルダ

" | "は "or"を意味する " {variant 1 | variant 2} "のようなプレースホルダを持つ文字列を持っています。私はプレースホルダのない文字列のすべての変形を取得したい。たとえば、 " {a | b {c | d}} "という文字列を使用すると、 " a "、 " "と" bd "と入力します。 再帰を使って正規表現 \ {([^ {}])\} で取得しようとしました(私の場合 {c | d} )次のステップでは2つの文字列があります: {a | bc} {a | bd} は " a " bc "、 " a "、 " bd "私はいくつかのグラフやツリー構造を作成する必要がありますか? 私はまた、(?[^ {} | $] *) なぜ "$"があるのですか?私はそれを削除し、何の効果もありません。

3
"{a | b {c | d}" "" {
追加された 著者 dfsq,
$ string = str_replace( '|'、 '、' $ string); var_dump( `/ bin/bash -c 'echo $ string'`); :-D
追加された 著者 cmbuckley,
申し訳ありません:)はい、正しい構文が必要です。私は投稿を編集します。
追加された 著者 Guy Fawkes,

2 答え

| {}が予約文字であると仮定すると(バリアントの内容として許可されていないので、以下は正規表現による問題へのアプローチです)、単純なステートマシンパーサーを書く方が良いでしょう。

<?php//Using PHP5.3 syntax

// PCRE Recursive Pattern
// http://php.net/manual/en/regexp.reference.recursive.php

$string = "This test can be {very {cool|bad} in random order|or be just text} ddd {a|b{c|d}} bar {a|b{c{d|e|f}}} lala {b|c} baz";

if (preg_match_all('#\{((?>[^{}]+)|(?R))+\}#', $string, $matches, PREG_SET_ORDER)) {
    foreach ($matches as $match) {
       //$match[0] == "{a|b{c|d}}" | "{a|b{c{d|e|f}}}" | "{b|c}"
       //have some fun splitting them up
       //I'd suggest walking the characters and building a tree
       //a simpler (slower, uglyer) approach:

       //remove {}
        $set = substr($match[0], 1, -1);
        while (strpos($set, '{') !== false) {
           //explode and replace nested {}
           //reserved characters: "{" and "}" and "|"
           //(?<=^|\{|\|) -- a substring needs to begin with "|" or "{" or be the start of the string,
           // "?<=" is a positive look behind assertion - the content is not captured
           //(?[^{|]+) -- is the prefix, preceeding literal string (anything but reserved characters)
           //\{(?[^{}]+)\} -- is the content of a nested {} group, excluding the "{" and "}"
           //(?[^|}$]*) -- is the postfix, trailing literal string (anything but reserved characters)
           //readable: {}
            $set = preg_replace_callback('#(?<=^|\{|\|)(?[^{}|]*)\{(?[^{}]+)\}(?[^{}|$]*)#', function($m) {
                $inner = explode('|', $m['inner']);
                return $m['prefix'] . join($inner, $m['postfix'] . '|' . $m['prefix']) . $m['postfix'];
            }, $set);
        }

       //$items = explode('|', $set);
        echo "$match[0] expands to {{$set}}\n";
    }
}

/*
    OUTPUT:
    {very {cool|bad} in random order|or be just text} expands to {very cool in random order|very bad in random order|or be just text}
    {a|b{c|d}} expands to {a|bc|bd}
    {a|b{c{d|e|f}}} expands to {a|bcd|bce|bcf}
    {b|c} expands to {b|c}
*/
1
追加された
あなたの例は、後続の文字の可能性を表現していませんでした。私はpostfixを説明する正規表現を変更しました - それは今あなたが期待することを行う必要があります。
追加された 著者 rodneyrehm,
ここでは$は意味がありません。私はちょうどそれらの存在を私に思い出させるために、(該当する場合)セットに^と$を入れたい。あなたはそれを捨てるかもしれない:)
追加された 著者 rodneyrehm,
それはクールに見えるが、string $ string = "このテストはランダムな順序で、またはテキストだけで{{{{{{{{{{cool | bad}}単にテキスト}は{非常にクール|ランダムな順序で非常に悪い|または単にテキストである}に展開する}
追加された 著者 Guy Fawkes,
私はこのエラーを修正することができます...どのようにregexp#(?<= ^ | \ {| \ |)(?<プレフィックス> [^ {|] +)\ { [^ {}] +)\}#?
追加された 著者 Guy Fawkes,
どうもありがとうございました!しかし、なぜあなたは後置式の "exclude symbols"グループで "$"を使うのか説明できますか?
追加された 著者 Guy Fawkes,
ああ、ありがとう!初心者のためのベストプラクティスを書いてください! :)
追加された 著者 Guy Fawkes,

このコードをチェックしてください:

$str = "This test can be {very {cool|bad} in random order|or be just text}";

function parseVarians($str, $buffer = array()) {
    if (empty($buffer)) $buffer['tokens'] = array();
    $newStr = preg_replace_callback('|\{([^{}]+)\}|', function($m) use(&$buffer) {
        $buffer['tokens'][] = explode('|', $m[1]);
        $index = count($buffer['tokens']) - 1;
        return '__' . $index;
    }, $str);

    if ($str != $newStr && strpos($newStr, '{') !== false) {
        return parseVarians($newStr, $buffer);
    }
    else {
        $buffer['str'] = $newStr;
        return $buffer;
    }
}

function devergeVariants($data) {
    krsort($data['tokens']);
    $strings  = array($data['str']);

    foreach ($data['tokens'] as $key => $token) {
        $variants = array();
        foreach ($token as $tok) {
            foreach ($strings as $str) {
                $variants[] = str_replace('__' . $key, $tok, $str);
            }
        }
        $strings = $variants;
    }

    return array_unique($strings);
}

echo '
'; print_r($str); echo '
'; $tokens = parseVarians($str); //echo '
'; print_r($tokens); echo '
'; $result = devergeVariants($tokens); echo '
'; print_r( $result ); echo '
';

出力:

This test can be {very {cool|bad} in random order|or be just text}
Array
(
    [0] => This test can be very cool in random order
    [1] => This test can be or be just text
    [2] => This test can be very bad in random order
)

あなたが望むように思える?

0
追加された
重複..はい。これは私のアルゴリズムの欠点です。
追加された 著者 dfsq,
見栄えが良いですが、array_uniqueを使用しています。実際には、$ strings配列の "This test is or just text"と重複しています。
追加された 著者 Guy Fawkes,
PHP - 日本のコミュニティ [ja]
PHP - 日本のコミュニティ [ja]
4 参加者の

このグループではPHPについて話します。 パートナー:kotaeta.com