this my next question for regex. Honestly, i want to build animation for math multiply , when their input some formula, their will get animation to describe where is this number will counting, this part of my code.
$pattern = '([*])';
$replace = '||';
$stringAsli = (123*1.23*(0.12/8))*(123*512+40);
$replaceResult= str_replace($pattern,$replace,$stringAsli);
but i got result :
(123||1.23||(0.12/8))||(123||512+40)
i want result like this
(123||1.23||(0.12/8))*(123||512+40);
Extended Requirements
In this special case with quoted strings:
("123*1.23*n"*"(0.12/8)")*("123*512"+40)
should become
("123||1.23||n"$"(0.12/8)")*("123||512"+40)
In other words, still replace the *
inside the brackets, but if we're inside a quoted strings we replace with ||
, and if not we replace with $
To safely get into your parentheses, we need to use a recursive regex. Fortunately, PHP supports this. Do it like this (see the output of the online demo):
$mystring = "(123*1.23*(0.12/8))*(123*512+40)";
$nested_parentheses_regex="~\((?:[^()]++|(?R))*\)~";
$replaced = preg_replace_callback(
$nested_parentheses_regex,
function($m) { return str_replace("*","||",$m[0]);},
$mystring
);
echo $replaced;
See the output of the online demo.
Explanation
\((?:[^()]++|(?R))*\)
matches (potentially) nested parenthesespreg_replace_callback
function calls an anonymous function which replaces every *
with a ||
within the match (which is a set of nested parentheses.You now asked that inside the parentheses, when the asterisk is not inside quotes, we return $
Use this:
$mystring = '("123*1.23*n"*"(0.12/8)")*("123*512"+40)';
$nested_parentheses_regex ="~\((?:[^()]++|(?R))*\)~";
$replaced = preg_replace_callback($nested_parentheses_regex,
function($m) {
return preg_replace_callback(
'~"[^"]*"|(\*)~', // complete "quote" OR capture * to Group 1
function($n) {
// Not inside quotes?
if(isset($n[1])) return "\$";
else { // we matched a "full quote"
return str_replace("*","||",$n[0]);
}
},
$m[0] // this is a (full (set) of parentheses)
); // end inner callback
},
$mystring
); // end outer callback
echo $replaced;
If you are trying to replace only a literal *
within parenthesis that will get tricky and require a recursive regex pattern.
A recursive regex pattern for nested parenthesis looks something like \((?>(?>[^()]+)|(?R))*\)
.
\( # matches a literal (
(?> # starts an atomic group
(?> # starts an atomic group
[^()]+ # matches 1 or more characters that are not parenthesis
) # ends atomic group
| # allows an alternative pattern
(?R) # allows the pattern to be recursed
) # ends atomic group
* # signifies the pattern match 0 or more times
\) # matches a literal )
The pattern will allow you to get all of the matches of everything within parenthesis.
$pattern = '/\((?>(?>[^()]+)|(?R))*\)/';
$subject = '(123*1.23*(0.12/8))*(123*512+40)';
$replacements = $matches = array();
preg_match_all($pattern, $subject, $matches);
To do the additional replacements on all of these groups I decided to create an array of literal replacement pairs.
foreach($matches[0] as $match) {
$replacements[$match] = preg_replace('/[*]/', '||', $match);
}
This gives you an array in $replacements
that has a key of what to replace and a value of what to replace it with and you can use it in str_replace
against your original string.
$result = str_replace(array_keys($replacements), array_values($replacements), $subject);
Implemented a regex-free solution just for fun:
It might be even faster than regex ;-D
$str = '(123*1.23*(0.12/8))*(123*512+40)';
$depth = 0;
for ($i = 0; $i < strlen($str); ++$i) {
$char = $str[$i];
switch ($char) {
case '(':
++$depth;
break;
case ')':
--$depth;
break;
case '*':
if ($depth != 0) {
$str = substr_replace($str, '||', $i, 1);
++$i;
}
break;
}
}
echo $str;
Demo: http://ideone.com/IOpOl9