Заменить литеральную звездочку (*), но только в круглых скобках

это мой следующий вопрос для регулярного выражения. Честно говоря, я хочу создать анимацию для математического умножения, когда они вводят какую-то формулу, они получат анимацию, чтобы описать, где это число будет подсчитываться, это часть моего кода.

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

Другими словами, по-прежнему заменяем * внутри скобок, но если мы находимся внутри строк в кавычках, мы заменяем их на ||, а если нет, то заменяем на $.


person banzai    schedule 21.07.2014    source источник
comment
Вы хотите сказать, что хотите заменить * только тогда, когда оно находится внутри круглых скобок?   -  person zx81    schedule 21.07.2014
comment
Я полагаю, что вы имеете в виду preg_replace вместо str_replace в своем примере кода на основе полученного вами результата.   -  person Steve Buzonas    schedule 21.07.2014


Ответы (3)


Рекурсивное регулярное выражение и замена лямбда

Чтобы безопасно попасть в ваши скобки, нам нужно использовать рекурсивное регулярное выражение. К счастью, 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;

Посмотрите результаты демонстрации в Интернете.

Пояснение

  • Рекурсивное регулярное выражение \((?:[^()]++|(?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;
person zx81    schedule 21.07.2014
comment
Хорошее использование квантификатора ++, это именно то, что я пытался вспомнить, как это сделать, и вместо этого просто решил пойти другим путем. - person Steve Buzonas; 21.07.2014
comment
@Steve Buzonas: чтобы сделать его полезным для всех остальных, он называется притяжательным квантификатором: regular-expressions. info/possessive.html - person zerkms; 21.07.2014
comment
привет, спасибо @zx81, это действительно работает, но я снова получаю один случай, одну переменную, которую я добавляю, например: $mystring = '(123*1.23*n*(0.12/8))*(123*512+40)'; n равно * будет добавлено к значению n, и результат должен выглядеть так: '(123||1.23||n$(0.12/8))*(123||512+40)', * внутри заменяется на | | НО вне * заменены на $. Пожалуйста, помогите мне, это дело должно быть завершено сегодня. С Уважением. :) - person banzai; 21.07.2014
comment
Не проблема, дай мне минутку. :) - person zx81; 21.07.2014
comment
Хорошо, сделано!!! См. раздел Extended Requirements. :) Дайте мне знать, если это то, что вы хотите. - person zx81; 21.07.2014
comment
да, ур мастер регулярных выражений! Спасибо. - person banzai; 21.07.2014
comment
Спасибо, рад, что помогло. :) - person zx81; 21.07.2014

Реализовано решение без регулярных выражений просто для удовольствия:

Это может быть даже быстрее, чем регулярное выражение ;-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

person zerkms    schedule 21.07.2014
comment
Я был на полпути к тому, чтобы написать то же самое с if вместо switch. +1 Это решение будет работать лучше всего. - person Shiplu Mokaddim; 21.07.2014
comment
@shiplu.mokadd.im: моя первая ревизия тоже была с if, потом я понял, что switch — это то, что мы обычно используем для FSA (хотя это не настоящий и не лучший пример FSA) - person zerkms; 21.07.2014
comment
@shiplu.mokadd.im This solution will work best Да, это весело и будет работать, но почему это должно работать лучше всего? (Почему это будет работать лучше, чем мое решение с регулярным выражением?) - person zx81; 21.07.2014
comment
@zx81: честно говоря, их обоих трудно понять и поддерживать (без приличного количества комментариев) :-) - person zerkms; 21.07.2014
comment
@zerkms Ха, с моей точки зрения, эти рекурсивные скобки (или скобки, фигурные скобки и т. д.) регулярные выражения являются частью моего стандартного арсенала, поэтому мне понадобится только одна строка комментариев (// regex for nested pars), поскольку в остальном код короткий и очевидный. Так что для меня гораздо проще поддерживать, расширять и т. д., чем пользовательский парсер (и я думаю, что идея регулярных выражений состоит в том, чтобы избавить нас от написания пользовательских парсеров). Но да, дело в том, к чему привык! :) - person zx81; 21.07.2014
comment
@zx81: действительно - $matchNestedParens = "~\((?:[^()]++|(?R))*\)~"; --- и вам даже не нужен комментарий (и даже посмотрите на регулярное выражение, когда вы читаете этот код) - person zerkms; 21.07.2014
comment
@zerkms Ты прав, это отличный способ обойтись без комментариев. Отличная мысль, спасибо. :) Эй, кстати, он добавил к требованиям: когда мы внутри "quotes" внутри скобок, мы все еще заменяем на ||, но в противном случае (все еще внутри скобок) мы заменяем на $... Только что сделал версию регулярного выражения, давая вам на всякий случай, если вы хотите сделать парсер. :) - person zx81; 21.07.2014
comment
@zx81: zx81: туда легко добавить, но я ушел. Пробный период для меня по написанию кода бесплатно в этом вопросе закончен :-) - person zerkms; 21.07.2014
comment
Я слышу тебя. Приятно поболтать, спасибо за ваши мысли, увидимся в другой раз. :) - person zx81; 21.07.2014

Если вы пытаетесь заменить только литерал * в круглых скобках, это будет сложно и потребует рекурсивного шаблона регулярного выражения.

Шаблон рекурсивного регулярного выражения для вложенных скобок выглядит примерно так: \((?>(?>[^()]+)|(?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);
person Steve Buzonas    schedule 21.07.2014
comment
Эй, Стив, к вашему сведению, внешние скобки в вашем рекурсивном регулярном выражении не служат никакой цели, их можно убрать. Также для (?> используется термин atomic group (когда вы говорите non-capturing group, люди понимают (?: - person zx81; 21.07.2014