我想匹配所有以".htm"结尾的字符串,除非它以"foo.htm"结尾。我通常对正则表达式很熟悉,但是负向先行断言让我感到困惑。为什么这不起作用?
/(?!foo)\.htm$/i.test("/foo.htm"); // returns true. I want false.
我应该使用什么替代方案?我认为我需要一个“负回顾后发”表达式(如果JavaScript支持这种表达方式的话,但我知道它不支持)。
我想匹配所有以".htm"结尾的字符串,除非它以"foo.htm"结尾。我通常对正则表达式很熟悉,但是负向先行断言让我感到困惑。为什么这不起作用?
/(?!foo)\.htm$/i.test("/foo.htm"); // returns true. I want false.
我应该使用什么替代方案?我认为我需要一个“负回顾后发”表达式(如果JavaScript支持这种表达方式的话,但我知道它不支持)。
问题其实很简单,这样做就可以:
/^(?!.*foo\.htm$).*\.htm$/i.test("/foo.htm"); // returns false
.*foo\.htm
直到字符串结尾。因为前瞻不被消耗,所以外面的第二个 $ 实际上是被匹配的。 - IceMetalPunk.
之前。所以,你实际上得到的是“任何以.htm
结尾的东西,只要从该位置开始的前三个字符(.ht
)不是foo
”,这总是成立的。// Checks that the last 3 characters before the dot are not foo:
/(?!foo).{3}\.htm$/i.test("/foo.htm"); // returns false
/(^.{0,2}|(?!foo).{3})\.htm$/i
。 - gilly3/(?!foo).{3}\.htm$/i
无法匹配少于三个字符的名称,例如 a.htm
。这里有一个可以匹配所有名称的正则表达式:/^(?!.*foo\.htm$).*\.htm$/i
。 - ridgerunner如前所述,JavaScript 不支持负回顾断言。
但是,您可以使用一种解决方法:
/(foo)?\.htm$/i.test("/foo.htm") && RegExp.$1 != "foo";
.htm
结尾的内容,但是如果它匹配了foo.htm
,它将把"foo"
存储在RegExp.$1
中,因此您可以单独处理它。RegExp.$1
特性是非标准的。 - alxndr就像Renesis提到的那样,“lookbehind”在JavaScript中不受支持,因此可以考虑将两个正则表达式结合使用:
!/foo\.htm$/i.test(teststring) && /\.htm$/i.test(teststring)
可能这个回答来得有点晚,但我还是会把它留在这里,以防现在(在这个问题被问出7年6个月后)有人遇到同样的问题。
现在,ECMA2018标准已经包含了lookbehinds,并且至少在Chrome的最新版本中受支持。然而,您可以使用或不使用它们来解决这个谜题。
一种使用负向前瞻的解决方案:
Original Answer翻译成"最初的回答"
let testString = `html.htm app.htm foo.tm foo.htm bar.js 1to3.htm _.js _.htm`;
testString.match(/\b(?!foo)[\w-.]+\.htm\b/gi);
> (4) ["html.htm", "app.htm", "1to3.htm", "_.htm"]
一种使用负回顾后断言的解决方案:
testString.match(/\b[\w-.]+(?<!foo)\.htm\b/gi);
> (4) ["html.htm", "app.htm", "1to3.htm", "_.htm"]
一种具有(技术上)正向先行断言的解决方案:
testString.match(/\b(?=[^f])[\w-.]+\.htm\b/gi);
> (4) ["html.htm", "app.htm", "1to3.htm", "_.htm"]
所有这些正则表达式以不同的方式告诉JS引擎相同的事情,它们传递给JS引擎的消息类似于以下内容。
请在此字符串中查找满足以下条件的所有字符序列:
等等。
String.prototype.endsWith (ES6)
String.prototype.endsWith (ES6)
console.log( /* !(not)endsWith */
!"foo.html".endsWith("foo.htm"), // true
!"barfoo.htm".endsWith("foo.htm"), // false (here you go)
!"foo.htm".endsWith("foo.htm"), // false (here you go)
!"test.html".endsWith("foo.htm"), // true
!"test.htm".endsWith("foo.htm") // true
);
/(.|..|.*[^f]..|.*f[^o].|.*fo[^o])\.htm$/
的方式来模拟负回顾,但编程方法会更好。