有没有办法在javascript正则表达式中实现与否定的后向等效?我需要匹配一个不以特定字符集开头的字符串。
如果在字符串的开头找到匹配的部分,我似乎无法找到执行此操作的正则表达式。负向后看似是唯一的答案,但是javascript没有答案。
编辑:这是我想工作的正则表达式,但它不:
(?<!([abcdefg]))m
因此它将与“ jim”或“ m”中的“ m”匹配,但与“ jam”不匹配
后向断言得到了接受入ECMAScript规范在2018年。
正向后方用法:
console.log(
"$9.99 €8.47".match(/(?<=\$)\d+(\.\d*)?/) // Matches "9.99"
);
负向后使用:
console.log(
"$9.99 €8.47".match(/(?<!\$)\d+(?:\.\d*)/) // Matches "8.47"
);
平台支持:
- ✔️ V8
- ✔️谷歌浏览器62.0
- ✔️Microsoft Edge 79.0
- ✔️Node.js 6.0位于标志后面,而9.0没有标志
- ✔️Deno(所有版本)
- ✔️的SpiderMonkey
- ✔️Mozilla Firefox 78.0
- JavaScript️JavaScriptCore:苹果正在研究
- Apple️苹果Safari
- 🛠️iOS WebView(iOS + iPadOS上的所有浏览器)
- ❌Chakra:Microsoft一直在研究它,但是Chakra现在被V8抛弃了
- ❌Internet Explorer
- 79 79之前的Edge版本(基于EdgeHTML + Chakra的版本)
自2018年以来,Lookbehind Assertions已成为ECMAScript语言规范的一部分。
// positive lookbehind
(?<=...)
// negative lookbehind
(?<!...)
回答2018年之前
由于Javascript支持否定先行,一种实现方法是:
-
反转输入字符串
-
与反向正则表达式匹配
-
反转并重新格式化比赛
const reverse = s => s.split('').reverse().join('');
const test = (stringToTests, reversedRegexp) => stringToTests
.map(reverse)
.forEach((s,i) => {
const match = reversedRegexp.test(s);
console.log(stringToTests[i], match, 'token:', match ? reverse(reversedRegexp.exec(s)[0]) : 'Ø');
});
范例1:
以下@ andrew-ensley的问题:
test(['jim', 'm', 'jam'], /m(?!([abcdefg]))/)
输出:
jim true token: m
m true token: m
jam false token: Ø
范例2:
在@neaumusic注释之后(匹配max-height
但不匹配line-height
,令牌为height
):
test(['max-height', 'line-height'], /thgieh(?!(-enil))/)
输出:
max-height true token: height
line-height false token: Ø
假设您想查找所有int
不以unsigned
:
支持负向后看:
(?<!unsigned )int
不支持负面的后顾之忧:
((?!unsigned ).{9}|^.{0,8})int
基本上,想法是抓住n个前面的字符,并排除具有负前瞻性的匹配,但也要匹配没有前面n个字符的情况。(其中n是向后看的长度)。
所以有问题的正则表达式:
(?<!([abcdefg]))m
将转换为:
((?!([abcdefg])).|^)m
您可能需要与捕获组一起玩,以找到您感兴趣的字符串的确切位置,或者您想用其他东西替换特定部分。
Mijoja的策略适用于您的特定情况,但不适用于一般情况:
js>newString = "Fall ball bill balll llama".replace(/(ba)?ll/g,
function($0,$1){ return $1?$0:"[match]";});
Fa[match] ball bi[match] balll [match]ama
这是一个示例,其目标是匹配双精度1,但如果以“ ba”开头则不匹配。请注意,“ balll”一词-真正的后向应该抑制前两个l,但匹配第二对。但是,通过匹配前2个l,然后忽略该匹配为误报,正则表达式引擎将从该匹配的结尾开始,并忽略误报内的任何字符。
使用
newString = string.replace(/([abcdefg])?m/, function($0,$1){ return $1?$0:'m';});
您可以通过否定字符集来定义一个非捕获组:
(?:[^a-g])m
...将与每个m
NOT开头的那些字母相匹配。
这是我str.split(/(?<!^)@/)
为Node.js 8(不支持后向)实现的:
str.split('').reverse().join('').split(/@(?!$)/).map(s => s.split('').reverse().join('')).reverse()
作品?是的(unicode未经测试)。不愉快?是。
遵循Mijoja的想法,并借鉴JasonS暴露的问题,我有了这个想法;我检查了一下,但不确定自己,所以在js正则表达式中由比我更专业的人进行验证将是很棒的:)
var re = /(?=(..|^.?)(ll))/g
// matches empty string position
// whenever this position is followed by
// a string of length equal or inferior (in case of "^")
// to "lookbehind" value
// + actual value we would want to match
, str = "Fall ball bill balll llama"
, str_done = str
, len_difference = 0
, doer = function (where_in_str, to_replace)
{
str_done = str_done.slice(0, where_in_str + len_difference)
+ "[match]"
+ str_done.slice(where_in_str + len_difference + to_replace.length)
len_difference = str_done.length - str.length
/* if str smaller:
len_difference will be positive
else will be negative
*/
} /* the actual function that would do whatever we want to do
with the matches;
this above is only an example from Jason's */
/* function input of .replace(),
only there to test the value of $behind
and if negative, call doer() with interesting parameters */
, checker = function ($match, $behind, $after, $where, $str)
{
if ($behind !== "ba")
doer
(
$where + $behind.length
, $after
/* one will choose the interesting arguments
to give to the doer, it's only an example */
)
return $match // empty string anyhow, but well
}
str.replace(re, checker)
console.log(str_done)
我的个人输出:
Fa[match] ball bi[match] bal[match] [match]ama
原理是checker
在字符串中任意两个字符之间的每个点处调用,只要该位置是以下位置的起点:
---任何不需要的大小的子字符串(此处'ba'
为..
)(如果知道该大小,否则可能很难做)
--- ---或更小(如果它是字符串的开头): ^.?
然后,
---实际要查找的内容(在此处'll'
)。
在每次调用时checker
,都会进行测试,以检查之前的值ll
是否不是我们不想要的值(!== 'ba'
);如果是这样,我们将调用另一个函数,并且必须使用这个(doer
)才能在str上进行更改,如果目的是这个(或更笼统地说),则将输入必要的数据以进行手动处理扫描的结果str
。
在这里,我们更改了字符串,因此我们需要跟踪长度的差异,以便偏移所给定的位置replace
,这些位置都是在上计算的str
,而该位置本身从未改变。
由于原始字符串是不可变的,因此我们可以使用该变量str
存储整个操作的结果,但是我认为该示例由于替换而已经很复杂,使用另一个变量(str_done
)会更清楚。
我猜在性能上一定很苛刻:所有无意义地将“”替换为“” this str.length-1
,再加上执行者手动替换,这意味着需要大量切片……在上述特定情况下,可能通过仅将字符串切成一小段的方式将其分组,使其围绕我们要插入的位置[match]
并与.join()
它[match]
自身对齐。
另一件事是,我不知道它将如何处理更复杂的情况,也就是说,伪造后视的复杂值……长度可能是要获取的最有问题的数据。
并且,在中checker
,如果$ behind的非期望值有多种可能性,我们将不得不使用另一个正则表达式(要在外部进行缓存(创建))进行测试checker
,以避免生成相同的正则表达式对象在每次调用checker
)时都知道它是否是我们想要避免的东西。
希望我已经清楚了;如果不犹豫,我会尽力而为。:)
使用大小写,如果您想替换 m
为某些内容,例如将其转换为大写M
,则可以否定捕获组中的集合。
匹配([^a-g])m
,替换为$1M
"jim jam".replace(/([^a-g])m/g, "$1M")
\\jiM jam
([^a-g])
将匹配范围内的任何char not(^
) a-g
,并将其存储在第一个捕获组中,因此您可以使用进行访问$1
。
因此我们发现im
在jim
,取而代之的是iM
其结果jiM
。
如前所述,JavaScript现在允许回溯。在较旧的浏览器中,您仍然需要解决方法。
我敢打赌,如果没有向后看就能准确地提供结果,就找不到找不到正则表达式的方法。您所能做的就是与小组合作。假设您有一个regex (?<!Before)Wanted
,其中Wanted
您要匹配Before
的正则表达式是,该regex可以计算出不应该匹配的内容。您能做的最好的事情就是取消使用正则表达式Before
并使用正则表达式NotBefore(Wanted)
。期望的结果是第一组$1
。
就您而言Before=[abcdefg]
,这很容易被否定NotBefore=[^abcdefg]
。因此正则表达式将是[^abcdefg](m)
。如果需要的位置Wanted
,则也必须分组NotBefore
,以便所需的结果是第二组。
如果Before
模式的匹配项具有固定的长度n
,也就是说,如果模式不包含重复的标记,则可以避免取反该Before
模式并使用正则表达式(?!Before).{n}(Wanted)
,但是仍然必须使用第一个组或使用正则表达式(?!Before)(.{n})(Wanted)
并使用第二个表达式组。在此示例中,图案Before
实际上具有固定长度,即1,因此请使用regex(?![abcdefg]).(m)
或(?![abcdefg])(.)(m)
。如果您对所有比赛都感兴趣g
,请添加标志,请参阅我的代码段:
function TestSORegEx() {
var s = "Donald Trump doesn't like jam, but Homer Simpson does.";
var reg = /(?![abcdefg])(.{1})(m)/gm;
var out = "Matches and groups of the regex " +
"/(?![abcdefg])(.{1})(m)/gm in \ns = \"" + s + "\"";
var match = reg.exec(s);
while(match) {
var start = match.index + match[1].length;
out += "\nWhole match: " + match[0] + ", starts at: " + match.index
+ ". Desired match: " + match[2] + ", starts at: " + start + ".";
match = reg.exec(s);
}
out += "\nResulting string after statement s.replace(reg, \"$1*$2*\")\n"
+ s.replace(reg, "$1*$2*");
alert(out);
}
这有效地做到了
"jim".match(/[^a-g]m/)
> ["im"]
"jam".match(/[^a-g]m/)
> null
搜索并替换示例
"jim jam".replace(/([^a-g])m/g, "$1M")
> "jiM jam"
请注意,负向后搜索字符串必须为1个字符长,这样才能正常工作。
/(?![abcdefg])[^abcdefg]m/gi
是的,这是一个把戏。
文章标签:javascript , negative-lookbehind , regex
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!