gpt4 book ai didi

javascript - 正则表达式否定性环顾四周,相邻2个匹配项

转载 作者:搜寻专家 更新时间:2023-11-01 05:12:39 25 4
gpt4 key购买 nike

应该有人提出来的一个简单问题:

如果我运行以下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代替?

编辑:我要在两边都没有猫的情况下替换“dog”。在 catdogdogcat中,两只狗都满足此要求,因此应将其替换。显然,我不了解这些负面的环顾...

最佳答案

您的方法有两个问题。

  • 您的第一个先行记录必须是回首记录。 当您编写(?!cat)时,引擎会检查接下来的三个字符是cat,然后重置到开始的位置(这是看起来前面的方式),然后尝试将dog与这三个字符匹配。因此,前瞻不会添加任何内容:如果您可以匹配dog,则显然无法在同一位置匹配cat。您想要的是一种在后面的(?<!cat),用于检查前面的字符不是cat。不幸的是,JavaScript不支持向后看。
  • 您想在逻辑上对这两种环顾或。 在您的情况下,如果任一环视失败,都会导致模式失败。因此,必须满足这两个要求(在任一端都没有cat)。但是,您实际上想对此进行OR。如果支持lookbehinds,则看起来像(?<!cat)dog|dog(?!cat)(请注意,交替将整个模式分开)。但是正如我所说,不支持向后看。您似乎在第一个catdogdog位中似乎对两个环视对象进行了“或”运算的原因是,前面的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,它将是未经修饰的 catdog000

    第三个替换项删除了字符串末尾的多余 000

    回调解决方案

    如果您不喜欢准备正则表达式,并且不喜欢第二种选择,则可以使用稍微更简单的正则表达式和替换回调:
    text.replace(/(catdog)(?=cat)|dog/g, function(match, firstGroup) {
    return firstGroup ? firstGroup : "000"
    })

    对于 replace版本,将为每个匹配项调用提供的函数,并将其返回值用作替换字符串。函数的第一个参数是整个匹配项,第二个参数是第一个捕获组(如果该组不参与匹配,则为 undefined),依此类推...

    因此,在替换回调中,如果 000是未定义的(即匹配的 firstGroup选项),我们可以自由设置我们的 dog,或者如果存在的话(即匹配的 firstGroup选项)仅返回 catdogcat。这有点简洁,可能更容易理解。但是,调用该函数的开销使其变为 significantly slower(尽管是否重要取决于您要执行此操作的频率)。选择你喜欢的!

    关于javascript - 正则表达式否定性环顾四周,相邻2个匹配项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17340966/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com