【问题标题】:Replacing all occurences of a specific word which are not enclosed with the words OPEN and CLOSE?替换所有未包含在 OPEN 和 CLOSE 中的特定单词的出现?
【发布时间】:2011-11-26 12:11:45
【问题描述】:

我有以下字符串:

OPEN有人打招呼CLOSE我打招呼的人OPEN有人说 你好OPEN他们又打招呼了CLOSE我现在得走了虽然CLOSE你好!

我正在尝试匹配所有出现的 hello(未包含在 OPENCLOSE 词中)并将它们替换为另一个词,可能带有正则表达式和 PHP 的 preg_replace 函数(尽管我对其他方法持开放态度,因为我想不出任何方法)。

所以从上面的字符串中,下面会匹配(我用斜体将它们放在括号中以帮助您区分):

打开有人说你好CLOSE我说(你好)人打开有人说 你好OPEN他们又打招呼了CLOSE我现在得走了,虽然CLOSEhello)又来了!

不知道该怎么做。

编辑也许这会更好地阐明嵌套结构:

OPEN
text
CLOSE

OPEN 
text
  OPEN
   text
  CLOSE
text
CLOSE

正如您从上面看到的,hello 没有被注意到,因为它在 OPEN...CLOSE(因此它们被忽略)内,而其他没有被替换。

【问题讨论】:

  • 如果在您的示例中最后一个 CLOSE 之前有一个“hello”,您是否希望它匹配?
  • 为什么OPENOPEN里面的那个没变?如果这是一个错误,那么我找到了一种方法来执行此操作,如果不是,我需要了解更改脚本的原因。

标签: php regex preg-replace preg-replace-callback


【解决方案1】:

艾伦的回答效果很好。但是,由于我已经花时间编写它,这里有另一种使用回调函数和 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];
}

【讨论】:

    【解决方案2】:

    我将hellos 编号,所以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

    lookahead 使用递归子模式构造 (?1) 来尝试匹配零个或多个完整的嵌套 OPEN...CLOSE 结构在当前匹配的单词和字符串结尾之间。假设所有OPENs 和CLOSEs 都适当平衡,这意味着它刚刚匹配的hello\d 不在这样的结构中。

    【讨论】:

    • 我更喜欢你的——它不需要使用回调函数。 +1
    【解决方案3】:

    这是我的尝试,告诉我它是否适合你:

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

    【讨论】:

      猜你喜欢
      • 2014-10-27
      • 1970-01-01
      • 2013-10-15
      • 1970-01-01
      • 1970-01-01
      • 2020-10-31
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      相关资源
      最近更新 更多