Php 不在双引号内时替换preg_

Php 不在双引号内时替换preg_,php,regex,preg-replace,Php,Regex,Preg Replace,基本上,我想在句子中替换某些单词(例如,单词“tree”和单词“pizza”)。限制:当应替换的单词位于双引号之间时,不应执行替换 例如: The tree is green. -> REPLACE tree WITH pizza "The" tree is "green". -> REPLACE tree WITH pizza "The tree" is green. -> DONT REPLACE "The tree is" green. -> DONT REPLAC

基本上,我想在句子中替换某些单词(例如,单词“tree”和单词“pizza”)。限制:当应替换的单词位于双引号之间时,不应执行替换

例如:

The tree is green. -> REPLACE tree WITH pizza
"The" tree is "green". -> REPLACE tree WITH pizza
"The tree" is green. -> DONT REPLACE
"The tree is" green. -> DONT REPLACE
The ""tree is green. -> REPLACE tree WITH pizza
有可能用正则表达式实现这一点吗?我会数一数单词前双引号的数量,然后检查它是奇数还是偶数。但是在php中使用preg_replace是否可能

谢谢

//编辑:

目前,我的代码如下所示:

preg_replace("/tree/", "pizza", $sentence)
但这里的问题是用双引号实现逻辑。我试过这样的方法:

preg_replace("/[^"]tree/", "pizza", $sentence)
但这不起作用,因为它只检查单词前面是否有双引号。但上面有一些例子表明该检查失败。
导入是因为我只想用正则表达式解决这个问题。

正则表达式不是一个可以为每项工作完成所需功能的工具。您可以在一定程度上使用正则表达式,但对于嵌套引号中的所有情况,它仍然变得更加复杂

您可以在此处使用一个负前瞻

$text = preg_replace('/\btree\b(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)/i', 'pizza', $text);

正则表达式:

\b               the boundary between a word char (\w) and not a word char
 tree            'tree'
\b               the boundary between a word char (\w) and not a word char
(?!              look ahead to see if there is not:
 [^"]*           any character except: '"' (0 or more times)
  "              '"'
 (?:             group, but do not capture (0 or more times)
  (?:            group, but do not capture (2 times):
   [^"]*         any character except: '"' (0 or more times)
    "            '"'
  ){2}           end of grouping
 )*              end of grouping
 [^"]*           any character except: '"' (0 or more times)
 $               before an optional \n, and the end of the string
)                end of look-ahead
另一个选择是使用受控回溯,因为您可以在


这样做的目的是跳过引文中的内容。我首先将引号与除
之外的任何字符进行匹配,然后使子模式失败,并强制正则表达式引擎不使用
(*跳过)
(*失败)
回溯控制动词的其他替代项重试子字符串。

使用此模式
树(?=(?:(?):[^“]*”{2})*[^“]*$)
gm
选项

这是如何从头开始构建的:
tree(?=[^“]*”
“tree”,可以看到任意数量的非引号字符后跟引号
树(?=([^“]*”{2})
~两次
树(?=([^“]*”{2})*)
~尽可能多次
tree(?=(([^“]*”){2})*[^“]*)
~然后是可选的非引号字符
tree(?=(([^“]*”){2})*[^“]*$)
~到最后
tree(?=(?:(?:[^”]*”{2})*[^”]*$)
添加非捕获组


使用一些隐藏的正则表达式幂有一个方便的技巧:

~“*?”(*跳过)(*失败)|\b树\b~s

说明:

~                   # start delimiter (we could have used /, #, @ etc...)
"                   # match a double quote
.*?                 # match anything ungreedy until ...
"                   # match a double quote
(*SKIP)(*FAIL)      # make it fail
|                   # or
\btree\b            # match a tree with wordboundaries
~                   # end delimiter
s                   # setting the s modifier to match newlines with dots .
在实际的PHP代码中,您可能希望使用来转义正则表达式字符。下面是一个小片段:

$search = 'tree';
$replace = 'plant';
$input = 'The tree is green.
"The" tree is "green".
"The tree" is green.
"The tree is" green.
The ""tree is green.';

$regex = '~".*?"(*SKIP)(*FAIL)|\b' . preg_quote($search, '~') . '\b~s';
$output = preg_replace($regex, $replace, $input);
echo $output;

此项使用前瞻性匹配

$pattern = '~\btree\b(?=([^"]|("[^"]*"))*$)~im';

$str = '
The tree is green. -> REPLACE tree WITH pizza
"The" tree is "green". -> REPLACE tree WITH pizza
"The tree" is green. -> DONT REPLACE
"The tree is" green. -> DONT REPLACE
The ""tree is green. -> REPLACE tree WITH pizza';

echo "<pre>".preg_replace($pattern,"pizza",$str)."</pre>";
$pattern='~\btree\b(?=([^“]|)(“[^”]*”))~im';
$str='1
这棵树是绿色的。->用比萨饼代替这棵树
树是绿色的->用比萨饼代替树
“树”是绿色的。->不要替换
这棵树是绿色的。->不要替换
“树是绿色的。->用比萨饼代替树”;
echo“.preg_replace($pattern,“pizza”和$str)。”;
它查找
,如果找到,则仅当后跟字符时才与之匹配,这些字符不是双引号
[^”]
或引号组
“[^”]*”
,直到行末使用


我不想要一个绿色的比萨饼!圣诞快乐:-)

我正在构建一个JS minimizer,这个页面帮助我找到了正确的正则表达式。但是这个页面至今没有回答的问题是当一个带引号的字符串包含转义引号时该怎么办。我在找到食谱时将这个页面作为书签

/*
正则表达式组“NotBetween”。
*/
函数rgxgnot介于($chars,$sep=“|”)之间
{
$chars=爆炸($sep,$chars);
$NB=[];
foreach($chars作为$CHR){
//(*PRUNE)在转义$CHR时跨过$CHR;也就是说,前面有一个反斜杠。
$NB[]=“(?:$CHR(?:\\\\$CHR(*PRUNE)|.*?$CHR)”;
}
$NB=连接(“|”,$NB);
返回“(?:(?:$NB)(*跳过)(*失败))”;
}
函数jsIdReplace($search、$replace、$source)
{
$search=“”
//在…之间时跳过进一步的匹配。。。
//双qoutes或js正则表达式斜杠
.rgxgnot介于(“\x22 |\x27 | \/”)之间
//没有前面的“.”和结尾“:”时匹配(对象属性)

."|(?:(?这被标记为php,但您没有显示任何支持它是php的内容,请您显示您尝试过的代码,并显示您尝试在php中替换的字符串。我们不是来为您编写代码的,所以请帮助我们。请看我的更新。这确实看起来很神奇,并且按照我的预期工作。您可以向我解释一下吗?我看到它很有用“tree”这个词后面没有这个巨大的语句。但是它到底是什么呢?这里的所有答案都不适用于带有中文字符的字符串。我刚刚在phpstorm/php7中尝试了这个,得到了一个“悬空元字符”对*SKIP和*FAIL星号的错误。preg_replace自2013年以来有变化吗?正如我在另一个答案上所说的。这很神奇,工作起来也很好。你能解释一下单词树后面的部分吗?看起来很难。PHP不支持g选项。(我认为应该是这样,因为preg应该代表perl reg ex)@Zarazthuztra我不知道PHP,但我知道附件中的演示是有效的,OP确认了它。@Zarazthuztra PHP没有
g
修饰符。要匹配单个实例,可以使用
preg\u match()
,要匹配所有实例,只需使用不同的函数
preg\u match\u all()
。替换
preg\u replace())
默认情况下会替换所有发生的事件。我们可以通过使用第四个参数来限制它。@HamZa是的,我已经知道了所有这些。我正在尝试帮助回答。嘿,太好了!我在哪里可以读到更多关于这些隐藏功能的信息?@Jonny5 PHP手册没有描述PCRE库的所有功能。所以你最好还是阅读一下当然,它太长了。我基本上是通过在Stackoverflow周围闲逛来学习这些东西的……啊,阅读perl手册也可能是个好主意……它是如何工作的?它为什么不替换
中的
树呢
$pattern = '~\btree\b(?=([^"]|("[^"]*"))*$)~im';

$str = '
The tree is green. -> REPLACE tree WITH pizza
"The" tree is "green". -> REPLACE tree WITH pizza
"The tree" is green. -> DONT REPLACE
"The tree is" green. -> DONT REPLACE
The ""tree is green. -> REPLACE tree WITH pizza';

echo "<pre>".preg_replace($pattern,"pizza",$str)."</pre>";
/*
Regular expression group 'NotBetween'.
*/
function rgxgNotBetween($chars, $sep="|")
{
    $chars = explode($sep, $chars);

    $NB = [];

    foreach($chars as $CHR){
        //(*PRUNE) steps over $CHR when it is escaped; that is, preceded by a backslash.
        $NB[] = "(?:$CHR(?:\\\\$CHR(*PRUNE)|.)*?$CHR)";
    }

    $NB = join("|", $NB);

    return "(?:(?:$NB)(*SKIP)(*FAIL))";
}

function jsIdReplace($search, $replace, $source)
{
    $search = ""

    //SKIP further matching when between...
    //double or single qoutes or js regular expression slashes
    .rgxgNotBetween("\x22|\x27|\/")

    //match when NO preceding '.' and no ending ':' (object properties)
    ."|(?:(?<!\.)\b$search\b(?!:))"

    //but do match when preceding '?' or ':' AND ending ':' (ternary statements)
    ."|(?:(?<=\?|:)\b$search\b(?=:))";

    return preg_replace($search, $replace, $source);
}

function jsNoComments($source)
{
    //js comment markers NOT between quotes
    $NBQ = rgxgNotBetween("\x22|\x27");

    //block comments
    $source = preg_replace("#$NBQ|/\*.*?\*/#s", "", $source);

    //line comments; not preceded by backslash
    $source = preg_replace("#$NBQ|\h*(?<!\\\\)//.*\n?#", "", $source);

    return $source;
}