Reputation: 2540
with below sentence,
{Please|Just} make this {cool|awesome|random} test sentence {rotate {quickly|fast} and random|spin and be random}
I need to create a random() function which will give below output:-
Please make this cool test sentence rotate fast and random.
OR
Just make this random test sentence spin and be random.
I'm not sure how will I do this.
I've tried below but didn't get result.
echo spinningFunction($str);
function spinningFunction($str)
{
$output = "";
$pattern = "/\[.*?\]|\{.*?\}/";
preg_match_all($pattern, $str, $match);
$arr = array_map(function($value){
return explode("|", $value);
}, $match[1]);
foreach($arr[0] as $adj)
foreach($arr[1] as $name)
$output.= "{$adj} make this {$name} test sentence<br />";
return $output;
}
any help please?
EDIT:-
function spinningFunction($str)
{
$str = preg_replace_callback('/(\{[^}]*)([^{]*\})/im', "spinningFunction", $str);
return $str;
}
Will someone help me to achieve an array like below from above sentence:-
Array
(
[0] => Array
(
[0] => {Please|Just}
[1] => {cool|awesome|random}
[2] => {rotate {quickly|fast} and random|spin and be random}
)
)
Upvotes: 3
Views: 1473
Reputation: 4795
Here's how you could do it using as many levels of nesting as you like:
function rand_replace($str)
{
$pattern = "/{([^{}]*)}/";
while (preg_match_all($pattern, $str, $matches) > 0) {
for ($i = 0; $i < count($matches[1]); $i++)
{
$options = explode('|', $matches[1][$i]);
$rand_option = $options[rand(0, count($options)-1)];
$str = str_replace($matches[0][$i], $rand_option, $str);
}
}
return $str;
}
It matches the deepest nested alternation first, randomly replaces it with one of the options, then works its way out until the entire string has been randomised.
Output from a few runs using your example input:
Just make this random test sentence rotate fast and random
Please make this random test sentence spin and be random
Please make this cool test sentence rotate fast and random
Just make this random test sentence spin and be random
Please make this awesome test sentence spin and be random
Upvotes: 0
Reputation: 29337
You have to take care of nested matches (see recursive patterns in http://php.net/manual/en/regexp.reference.recursive.php). Try with this
function spinningFunction($str)
{
$output = "";
$pattern = "/{(?:[^{}]+|(?R))*}/";
preg_match_all($pattern, $str, $match);
print_r($match[0]);
}
$s = "{Please|Just} make this {cool|awesome|random} test sentence {rotate {quickly|fast} and random|spin and be random}";
spinningFunction($s);
Explanation of the pattern {(?:[^{}]+|(?R))*}
:
{ matches the character { literally
(?:[^{}]+|(?R))* Non-capturing group
Quantifier: Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
1st Alternative: [^{}]+
[^{}]+ match a single character not present in the list below
Quantifier: Between one and unlimited times, as many times as possible, giving back as needed [greedy]
{} a single character in the list {} literally
2nd Alternative: (?R)
(?R) recurses the entire pattern
} matches the character } literally
Upvotes: 1
Reputation: 4095
Here is what you're looking for:
Something like this:
$text = '{Please|Just} make this {cool|awesome|random} test sentence {rotate|spin) {quickly|fast} and {randomize|spin} the text';
echo 'input-text:<br />'.$text.'<br />'; //echo original string for text output purposes.
$openingConstruct = '{';
$closingConstruct = '}';
$separator = '|';
if(preg_match_all('!'.$openingConstruct.'(.*?)'.$closingConstruct.'!s', $text, $matches)){
$find = array();
$replace = array();
foreach($matches[0] as $key => $match){
$choices = explode($separator, $matches[1][$key]);
$find[] = $match;
$replace[] = $choices[mt_rand(0, count($choices) - 1)];
foreach($find as $key => $value){
if(($pos = mb_strpos($text, $value)) !== false) {
if(!isset($replace[$key])){
$replace[$key] = '';
}
$text = mb_substr($text, 0, $pos).$replace[$key].mb_substr($text, $pos + mb_strlen($value));
}
}
}
echo 'output-text:<br />'.$text;
}
Output:
input-text:
{Please|Just} make this {cool|awesome|random} test sentence {rotate|spin) {quickly|fast} and {randomize|spin} the text
output-text:
Just make this random test sentence rotate and randomize the text
Upvotes: 0
Reputation: 20486
Here's a solution that requires to use the syntax {a|[b|c]}
for nested sets. It also only goes one level deep manually, so there is no clean/simple recursion. Depending on your use case, this could be fine though.
function randomizeString($string)
{
if(preg_match_all('/(?<={)[^}]*(?=})/', $string, $matches)) {
$matches = reset($matches);
foreach($matches as $i => $match) {
if(preg_match_all('/(?<=\[)[^\]]*(?=\])/', $match, $sub_matches)) {
$sub_matches = reset($sub_matches);
foreach($sub_matches as $sub_match) {
$pieces = explode('|', $sub_match);
$count = count($pieces);
$random_word = $pieces[rand(0, ($count - 1))];
$matches[$i] = str_replace('[' . $sub_match . ']', $random_word, $matches[$i]);
}
}
$pieces = explode('|', $matches[$i]);
$count = count($pieces);
$random_word = $pieces[rand(0, ($count - 1))];
$string = str_replace('{' . $match . '}', $random_word, $string);
}
}
return $string;
}
var_dump(randomizeString('{Please|Just} make this {cool|awesome|random} test sentence {rotate [quickly|fast] and random|spin and be random}.'));
// string(53) "Just make this cool test sentence spin and be random."
var_dump(randomizeString('You can only go two deep. {foo [bar|foo]|abc 123}'));
// string(33) "You can only go two deep. foo foo"
Upvotes: 2