来自那里的人应该是一个简单的问题:
如果我运行此JavaScript:
var regex = new RegExp("(?!cat)dog(?!cat)","g");
var text = "catdogcat catdogdog catdogdogcat".replace(regex,"000");
console.log(text);
输出:
catdogcat cat000000 cat000dogcat
但是我应该输出这个:
catdogcat cat000000 cat000000cat
为什么catdogdogcat
中的第二只“狗”没有被000
取代?
编辑:只要两只猫没有猫,我想替换“狗”。在catdogdogcat
中,两只狗都满足了这个要求,所以应该更换它们。显然我不明白这些消极的看法...
答案 0 :(得分:7)
您的方法存在两个问题。
(?!cat)
时,引擎会检查接下来的三个字符是否为cat
,然后重置到它开始的位置(这就是看起来前进的方式),然后尝试将dog
与这三个相同的字符进行匹配。因此,前瞻不会添加任何内容:如果您可以匹配dog
,则显然无法在同一位置匹配cat
。你想要的是一个lookbehind (?<!cat)
,它检查前面的字符是不是cat
。不幸的是,JavaScript不支持lookbehind。cat
)的要求。但你实际上想要 OR 。如果支持看起来像(?<!cat)dog|dog(?!cat)
的看起来很好看(注意交替将整个模式分开)。但正如我所说,不支持lookbehinds。您似乎在第一个catdogdog
位中使用* OR *编辑两个外观的原因是前面的cat
没有被检查(参见第1点)。如何解决后视镜问题呢? Kolink的回答建议(?!cat)...dog
,它将看起来放在cat
开始的位置,并使用前瞻。这有两个新问题:它不能匹配字符串开头的dog
(因为前面的三个字符是必需的。它不能匹配两个连续的dog
因为匹配不能重叠(匹配后)第一个dog
,引擎需要三个...
新字符,这些字符会在再次实际匹配dog
之前使用下一个dog
。
有时你可以通过反转模式和字符串来解决这个问题,从而将外观转变为前瞻 - 但在你的情况下会将最后的前瞻变为后视。
我们必须更加聪明一点。由于匹配不能重叠,我们可以尝试显式匹配catdogcat
,而不替换它(因此在目标字符串中跳过它们),然后只替换我们找到的所有dog
。我们将这两个案例交替进行,因此它们都在字符串中的每个位置都进行了尝试(catdogcat
选项优先,尽管这里并不重要)。问题是如何获得条件替换字符串。但是让我们看看到目前为止我们得到了什么:
text.replace(/(catdog)(?=cat)|dog/g, "$1[or 000 if $1 didn't match]")
因此,在第一个备选方案中,我们匹配catdog
并将其捕获到组1
中,并检查是否还有其他cat
。在替换字符串中,我们只需将$1
写回。美丽的是,如果第二种选择匹配,第一组将是未使用的,因此是一个空的字符串替换。我们仅匹配catdog
并使用前瞻而不是立即匹配catdogcat
的原因再次是重叠匹配。如果我们使用catdogcat
,那么在输入catdogcatdogcat
中,第一个匹配将消耗所有内容,直到并包括第二个cat
,因此第一个匹配将无法被第一个dog
识别替代品。
现在唯一的问题是,如果我们使用第二种选择,我们如何获得替换000
。
不幸的是,我们无法想象不属于输入字符串的条件替换。诀窍是在输入字符串的末尾添加000
,如果我们找到dog
,则在前瞻中捕获它,然后将其写回:
text.replace(/$/, "000")
.replace(/(catdog)(?=cat)|dog(?=.*(000))/g, "$1$2")
.replace(/000$/, "")
第一次替换将000
添加到字符串的末尾。
第二个替换匹配catdog
(检查另一个cat
后跟)并将其捕获到组1
(将2
留空)或匹配dog
并将000
捕获到组2
中(将组1
留空)。然后我们写回$1$2
,这将是未加修饰的catdog
或000
。
第三个替换在字符串的末尾摆脱了我们无关的000
。
如果您不是准备正则表达式的粉丝,也不是第二个选项中的前瞻,那么您可以使用稍微简单的正则表达式和替换回调:
text.replace(/(catdog)(?=cat)|dog/g, function(match, firstGroup) {
return firstGroup ? firstGroup : "000"
})
使用replace
的版本,为每个匹配调用所提供的函数,并将其返回值用作替换字符串。函数first参数是整个匹配,第二个参数是第一个捕获组(如果组没有参与匹配,则为undefined
)等等......
所以在替换回调中,如果000
未定义(即firstGroup
选项匹配),我们可以自由地召唤我们dog
,或者只返回firstGroup
存在(即匹配的catdogcat
选项)。这有点简洁,可能更容易理解。但是,调用该函数的开销使其成为significantly slower(尽管这是否重要取决于您想要执行此操作的频率)。选择你最喜欢的!
答案 1 :(得分:1)
您的正则表达式简化为dog(?!cat)
(因为第一个lookbehind不会消耗任何内容),因此它会替换dog
之后没有cat
的任何实例。
试用正则表达式(?!cat).{3}dog(?!cat)