I have the following string:
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!
I'm trying to match all occurences of hello (that are not enclosed in the OPEN and CLOSE words) and replace them with another word, possibly with a regex and PHP's preg_replace
function (although I'm open to other methods as I can't think of any).
So from the above string the below will match (I've placed them in brackets with italics to help you distinguish):
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!
Not really sure how to go by doing this.
Edit perhaps this will clarify the nesting structure abit better:
OPEN
text
CLOSE
OPEN
text
OPEN
text
CLOSE
text
CLOSE
As you can see from above, the hello is not being notice because its within OPEN...CLOSE (so they are ignored) whereas the others which arent are going to be replaced.
Alan's answer works great. However, since I already took the time to compose it, here is another way to do it using a callback function and the PHP (?R)
recursive expression:
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];
}