替换文字星(*)...但仅在括号内

时间:2014-07-21 02:11:08

标签: php regex recursion

这是关于正则表达式的下一个问题。 老实说,我想为数学乘法建立动画,当他们输入一些公式时,他们会得到动画来描述这个数字将在哪里计算,这部分是我的代码。

$pattern = '([*])';
$replace = '||';
$stringAsli = (123*1.23*(0.12/8))*(123*512+40);
$replaceResult= str_replace($pattern,$replace,$stringAsli);

但我得到了结果:

(123||1.23||(0.12/8))||(123||512+40)

我想要这样的结果

(123||1.23||(0.12/8))*(123||512+40);

扩展要求

在带引号字符串的特殊情况下:

("123*1.23*n"*"(0.12/8)")*("123*512"+40)

应该成为

("123||1.23||n"$"(0.12/8)")*("123||512"+40)

换句话说,仍然会替换括号内的*,但如果我们在引用的字符串中替换为||,如果不是,则替换为$

3 个答案:

答案 0 :(得分:3)

递归正则表达式和Lambda替换

为了安全地进入括号,我们需要使用递归正则表达式。幸运的是,PHP支持这一点。这样做(参见在线演示的输出):

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

查看the online demo的输出。

<强>解释

  • 递归正则表达式\((?:[^()]++|(?R))*\)匹配(可能)嵌套括号
  • preg_replace_callback函数调用一个匿名函数,该函数用匹配中的*替换每个||(这是一组嵌套括号。

扩展要求

您现在在括号内询问,当星号不在引号内时,我们返回$

使用此:

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

答案 1 :(得分:2)

为了好玩,实施了一个无正则表达式的解决方案:

它可能比正则表达式更快;-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;

演示:http://ideone.com/IOpOl9

答案 2 :(得分:1)

如果您尝试仅替换括号内的文字*,那将会变得棘手且需要递归的正则表达式模式。

嵌套括号的递归正则表达式模式类似于\((?>(?>[^()]+)|(?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 )

该模式将允许您获取括号内所有内容的所有匹配。

$pattern = '/\((?>(?>[^()]+)|(?R))*\)/';
$subject  = '(123*1.23*(0.12/8))*(123*512+40)';

$replacements = $matches = array();

preg_match_all($pattern, $subject, $matches);

为了对所有这些组进行额外的替换,我决定创建一个文字替换对的数组。

foreach($matches[0] as $match) {
    $replacements[$match] = preg_replace('/[*]/', '||', $match);
}

这为您提供了$replacements中的数组,其中包含要替换的内容的键以及替换内容的值,您可以在str_replace中使用它来对应原始字符串。

$result = str_replace(array_keys($replacements), array_values($replacements), $subject);