Заменить все вхождения конкретного слова, которые не заключены в слова ОТКРЫТЬ и ЗАКРЫТЬ? - PullRequest
3 голосов
/ 28 сентября 2011

У меня есть следующая строка:

ОТКРЫТО кто-то сказал привет ЗАКРЫТЬ я говорю привет людям ОТКРЫТЬ некоторые сказали привет ОТКРЫТЬ они снова поздоровались ЗАКРЫТЬ Мне нужно идти сейчас, хотя ЗАКРЫТЬ Привет еще раз!

Я пытаюсь сопоставить все случаииз привет (которые не заключены в слова OPEN и CLOSE ) и замените их другим словом, возможно, с помощью регулярного выражения и функции PHP preg_replace (хотя я открыт для других методов, так как не могу придумать ни одного).

Так что из приведенной выше строки будет соответствовать нижеследующее (я поместил их в скобки с курсивом, чтобы помочь вам отличить их):

ОТКРЫТО кто-то сказал привет ЗАКРЫТЬ я говорю ( привет ) люди ОТКРЫТЬ некоторые сказали привет ОТКРЫТЬ они снова поздоровались ЗАКРЫТЬ мне нужно идти сейчас, хотя ЗАКРЫТЬ ( привет ) снова!

Не совсемsКак это сделать.

Редактируйте, возможно, это лучше прояснит структуру вложенности:

OPEN
text
CLOSE

OPEN 
text
  OPEN
   text
  CLOSE
text
CLOSE

Как вы можете видеть сверху, привет не замечается, потому что его внутриОТКРЫТЬ ... ЗАКРЫТЬ (поэтому они игнорируются), тогда как остальные, которые не будут заменены.

Ответы [ 3 ]

2 голосов
/ 28 сентября 2011

Я пронумеровал hello с, поэтому hello2 и hello5 - это те, которые должны быть заменены.

$s0 = 'OPEN someone said hello1 CLOSE im saying hello2 people OPEN some said hello3 OPEN they said hello4 again CLOSE i have to go now though CLOSE hello5 again!';

$regex='~
hello\d
(?=
  (?:(?!OPEN|CLOSE).)*+
  (?:
    ( 
      OPEN
      (?:
        (?:(?!OPEN|CLOSE).)*+
        |
        (?1)
      )*
      CLOSE
    )
    (?:(?!OPEN|CLOSE).)*+
  )?
  $
)
~x';

$s1=preg_replace($regex, 'goodbye', $s0);
print($s1);

вывод:

OPEN someone said hello1 CLOSE im saying goodbye people OPEN some said hello3 OPEN they said hello4 again CLOSE i have to go now though CLOSE goodbye again!

demo

Предпросмотр использует рекурсивную конструкцию подшаблона, (?1), чтобы попытаться сопоставить ноль или более полных, вложенных OPEN...CLOSE структур между текущим совпавшим словом и концомстроки.Предполагая, что все OPEN s и CLOSE s сбалансированы должным образом, это означает, что hello\d, который он только что сопоставил, не внутри такой структуры.

2 голосов
/ 28 сентября 2011

Ответ Алана прекрасно работает. Однако, поскольку я уже нашел время для его составления, есть еще один способ сделать это, используя функцию обратного вызова и рекурсивное выражение PHP (?R):

function highlightNonNestedHello($str) {
    $re = '/# Two global alternatives. Either...
          (                          # $1: Non-O..C stuff.
            (?:                      # Step through non-O..C chars.
              (?!\b(?:OPEN|CLOSE)\b) # If not start of OPEN or CLOSE,
              .                      # then match next char.
            )+                       # One or more non-O..C chars.
          )                          # End $1:
        |                            # Or...
          (                          # $2: O..C stuff.
            \bOPEN\b                 # Open literal delimiter.
            (?R)+                    # Recurse overall regex.
            \bCLOSE\b                # Close literal delimiter.
          )                          # End $1:
    /sx';
    return preg_replace_callback($re, '_highlightNonNestedHello_cb', $str);
}
function _highlightNonNestedHello_cb($matches) {
    // Case 1: Non-O...C stuff. Highlight all "hello".
    if ($matches[1]) {
        return preg_replace('/\bhello\b/', '(HELLO)', $matches[1]);
    }
    // Case 2: O...C stuff. Preserve as-is.
    return $matches[2];
}
0 голосов
/ 28 сентября 2011

Ну, это моя попытка, скажите, работает ли она у вас или нет:

<?php

$str = 'OPEN someone said hello CLOSE im saying hello people OPEN some said hello OPEN they said hello again CLOSE i have to go now though CLOSE hello again!';
echo "<p>$str</p>"; //before

//first replace all of them
$str = str_replace('hello', '(hello)', $str);
//then replace back only those within OPEN CLOSE
function replace_back($match){return str_replace('(hello)', 'hello', $match[0]);}
$str = preg_replace_callback('/OPEN.*?\(hello\).*?CLOSE/', 'replace_back', $str); 

echo "<p>$str</p>"; //after

?>
<style>p{width:500px;background:#F1F1F1;padding:10px;font:13px Arial;}</style>
...