3

Lets say I have:

$line = "{This is my {sentence|words} I wrote.}"

Output:

This is my sentence I wrote.  
This is my words I wrote.  

But, the regex should match deep and nested values and split these values, for example:

$line = "{This is my {sentence|words} I wrote on a {sunny|cold} day.}";

Output:

This is my sentence I wrote on a sunny day.  
This is my sentence I wrote on a cold day.  
This is my words I wrote on a sunny day.
This is my words I wrote on a cold day.  

My first though was doing it over explode as in code below, but the result was not appropriate:

$res = explode("|", $line);  

Advices? Thank you.

EDIT: Something in these lines:

$line = "{This is my {sentence|words} I wrote on a {sunny|cold} day.}";
$regex = "{[^{}]*}";
$match = [];

preg_match($regex, $line, $match);

var_dump($match);  

As already said, it can go to an infinite so no limit, something in a for-loop appropriate.

J. Doe
  • 93
  • 6
  • 1
    I think you can create a function that _replaces_ the first match of [`/{[^{}]*}/`](https://regex101.com/r/zV9sX0/1) and returns the match and its index... Then, while the return is not -1, you keep exploding by `|`... Of course you will need an array to push each new _sentence_ (composed by one of the exploded values inserted in the index returned by the function) –  Mar 30 '16 at 13:45
  • $res = explode("|", $line, 2); – MaThar Beevi Mar 30 '16 at 13:48
  • I guess the number of nested levels can be any, right? Like `$line = "{This is my {sentence|words} I wrote on a {{very|not so} sunny|{freezing|rather} cold} day.}";` – Wiktor Stribiżew Mar 30 '16 at 15:15
  • @WiktorStribiżew indeed. – J. Doe Mar 30 '16 at 20:25

1 Answers1

3

Check this out. I accomplished it by replacing your patterns with %s and using vsprintf, then recursively looping through the matches.

I put a lot of comments in the code...understanding recursion is usually quite a mind job.

Here is a working example.

$line = "{This is my {sentence|statement} I {wrote|typed} on a {hot|cold} {day|night}.}";
$matches = getMatches($line);
printWords([], $matches, $line);


// function to find patterns in the line. Takes $line by reference to replace pattern matches with a vsprintf placeholder
function getMatches(&$line) {
    // remove beginning and trailing brackets on the main sentence
    $line = trim($line, '{}'); 

    // initialize variable that will hold the list of pattern matches
    $matches = null;

    // look for an opening curly brace and skip everything until the ending curly brace
    $pattern = '/\{[^}]+\}/';

    // find all matches and put them in $matches
    preg_match_all($pattern, $line, $matches);

    // preg_match_all nests one level deeper than we need
    $matches = $matches[0];

    // replace all matches with a %s placeholder
    $line = preg_replace($pattern, '%s', $line);

    // split each of the matches by vertical pipe
    foreach ($matches as $index => $match) {
        $matches[$index] = explode('|', trim($match, '{}'));
    }

    return $matches;
}


// recursive function. $args will be used as the second argument to vsprintf
function printWords(array $args, array $matches, $line) {
    // get the first element in the array of $matches, remove it from the array
    $current = array_shift($matches);

    // keep track of the current $args index for this recursive iteration
    $currentArgIndex = count($args);

    // loop through each of the words in the current set of matches
    foreach ($current as $word) {
        // update $args and set the vsprintf argument at this iteration's position to the next word in the set of words
        $args[$currentArgIndex] = $word;

        if (!empty($matches)) {
            // repeat this process (recursively) until we are at the end of the list of matches
            printWords($args, $matches, $line);
        } else {
            // if this is the last match in the line, echo the sentence with all args from previous recursive iterations added
            echo vsprintf($line, $args) . '<br />';
        }
    }
}

Outputs:

This is my sentence I wrote on a hot day.
This is my sentence I wrote on a hot night.
This is my sentence I wrote on a cold day.
This is my sentence I wrote on a cold night.
This is my sentence I typed on a hot day.
This is my sentence I typed on a hot night.
This is my sentence I typed on a cold day.
This is my sentence I typed on a cold night.
This is my statement I wrote on a hot day.
This is my statement I wrote on a hot night.
This is my statement I wrote on a cold day.
This is my statement I wrote on a cold night.
This is my statement I typed on a hot day.
This is my statement I typed on a hot night.
This is my statement I typed on a cold day.
This is my statement I typed on a cold night.
Travesty3
  • 14,351
  • 6
  • 61
  • 98
  • Crazy. Love your solution. – J. Doe Mar 30 '16 at 22:46
  • Nice solution, although this doesn't work for nested levels. – James Buck Mar 30 '16 at 22:49
  • @JamesBuck: You are correct. And while the OP did use the word "nested" in his question, I didn't see any examples that suggested he actually needed that, nor can I really think of an example when you would need nested levels for this type of problem. So I figured he didn't really mean "nested". – Travesty3 Mar 31 '16 at 13:25
  • @Travesty3 I didn't. But I'm still curious for a way on a nested value using regex. For e.g. line like this: `{ It is { raining { and streets are wet } | snowing { and streets are { slippy | white }}}`. Tomorrow will be nice { weather | walk }. }`, and the output should be like [this](http://pastebin.com/raw/m8ftGwJE). I can figure out how to deal with space but not with nested level. – J. Doe Apr 03 '16 at 21:51