替换所有未包含在 OPEN 和 CLOSE 中的特定单词的出现?

Posted

技术标签:

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

我有以下字符串:

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

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

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

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

不知道该怎么做。

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

OPEN
text
CLOSE

OPEN 
text
  OPEN
   text
  CLOSE
text
CLOSE

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

【问题讨论】:

如果在您的示例中最后一个 CLOSE 之前有一个“hello”,您是否希望它匹配? 为什么OPENOPEN里面的那个没变?如果这是一个错误,那么我找到了一种方法来执行此操作,如果不是,我需要了解更改脚本的原因。 【参考方案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>pwidth:500px;background:#F1F1F1;padding:10px;font:13px Arial;</style>

【讨论】:

以上是关于替换所有未包含在 OPEN 和 CLOSE 中的特定单词的出现?的主要内容,如果未能解决你的问题,请参考以下文章

对#include <iconv.h>、`libiconv_open'、`libiconv'、`libiconv_close' 的未定义引用

Python数据框-如何选择名称中包含特定子字符串的所有列[重复]

Dijkstra算法

WPF - 按钮的内容未显示

Linux基礎知識 —— open&close

替换元素和非替换元素