2011-09-28 78 views
3

我有以下字符串:替换OPEN和CLOSE字未包含的特定单词的所有出现?

OPEN有人说你好CLOSE即时消息说你好的人OPEN有的说 你好OPEN他们又打着招呼CLOSE我现在虽然去关闭你好!

我想匹配你好出现的所有(未封闭在OPENCLOSE的话),并用另一个词替换它们,可能与正则表达式和PHP的preg_replace功能(尽管我对其他方法开放,我想不出任何)。

所以从上面的字符串下面将匹配(我已经把他们安置在括号内用斜体来帮助你区分):

OPEN有人说你好CLOSE即时消息说,(你好 )人OPEN有的说 你好OPEN他们又打着招呼CLOSE我现在得走了,虽然CLOSEhello)!

不确定如何去做这件事。

编辑也许这将更好地阐明嵌套结构升技:

OPEN 
text 
CLOSE 

OPEN 
text 
    OPEN 
    text 
    CLOSE 
text 
CLOSE 

正如你可以看到从上面的招呼没有被通知,因为其内打开... CLOSE(所以他们被忽略),而其他没有的将被替换。

+0

如果有一个“你好”之前在最后关闭例如,你想要它匹配还是不匹配? – Amber

+0

@Amber不匹配 – user962026

+0

为什么OPEN' OPEN'中的那个没有改变?如果这是一个错误,那么我找到了一个方法来做到这一点,如果不是我需要理解改变我的脚本的理由。 – derp

回答

2

艾伦的答案很好。但是,因为我已经把组成它的时候,这里是另一种方式来做到这一点使用回调函数和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

嗯,这是我的尝试,告诉我,如果你的作品或不:

<?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> 
2

我编号为hello S,所以hello2hello5是应该被替换的人。

$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结构和字符串的结尾。假设所有的OPENs和CLOSEs都被适当地平衡,这意味着它刚刚匹配的hello\d而不是内部这样的结构。

+0

我更喜欢你的 - 它不需要使用回调函数。 +1 – ridgerunner

+0

...但在你的正则表达式更容易阅读。 –

相关问题