我正在编写一组正则表达式来将 CSS 选择器转换为 id 和类数组。
例如,我希望 '#foo#bar' 返回 ['foo', 'bar']。
我一直在努力实现这一目标
"#foo#bar".match(/((?:#)[a-zA-Z0-9\-_]*)/g)
但是当非捕获前缀 ?: 应该忽略 # 字符时,它会返回 ['#foo', '#bar']。
有没有比对每个返回的字符串进行切片更好的解决方案?
我正在编写一组正则表达式来将 CSS 选择器转换为 id 和类数组。
例如,我希望 '#foo#bar' 返回 ['foo', 'bar']。
我一直在努力实现这一目标
"#foo#bar".match(/((?:#)[a-zA-Z0-9\-_]*)/g)
但是当非捕获前缀 ?: 应该忽略 # 字符时,它会返回 ['#foo', '#bar']。
有没有比对每个返回的字符串进行切片更好的解决方案?
您可以使用.replace()
or.exec()
在循环中构建一个数组。
与.replace()
:
var arr = [];
"#foo#bar".replace(/#([a-zA-Z0-9\-_]*)/g, function(s, g1) {
arr.push(g1);
});
与.exec()
:
var arr = [],
s = "#foo#bar",
re = /#([a-zA-Z0-9\-_]*)/g,
item;
while (item = re.exec(s))
arr.push(item[1]);
它匹配#foo
并且#bar
因为外部组(#1)正在捕获。内部组(#2) 不是,但这可能不是您要检查的。
如果您没有使用全局匹配模式,则立即修复将改为使用(/(?:#)([a-zA-Z0-9\-_]*)/
。
使用全局匹配模式,结果不能只在一行中得到,因为match
行为不同。仅使用正则表达式(即没有字符串操作),您需要这样做:
var re = /(?:#)([a-zA-Z0-9\-_]*)/g;
var matches = [], match;
while (match = re.exec("#foo#bar")) {
matches.push(match[1]);
}
我不确定你是否可以使用 match() 来做到这一点,但你可以使用 RegExp 的 exec() 方法来做到这一点:
var pattern = new RegExp('#([a-zA-Z0-9\-_]+)', 'g');
var matches, ids = [];
while (matches = pattern.exec('#foo#bar')) {
ids.push( matches[1] ); // -> 'foo' and then 'bar'
}
您可以使用否定的前瞻断言:
"#foo#bar".match(/(?!#)[a-zA-Z0-9\-_]+/g); // ["foo", "bar"]
不幸的是,Javascript RegExp 中没有后向断言,否则你可以这样做:
/(?<=#)[a-zA-Z0-9\-_]*/g
除了将它添加到一些新版本的 Javascript 之外,我认为使用split
后处理是你最好的选择。
几年前 mVChr 提到的后向断言被添加到 ECMAScript 2018中。这将允许您这样做:
'#foo#bar'.match(/(?<=#)[a-zA-Z0-9\-_]*/g)
(返回["foo", "bar"]
)
(也可以使用否定的lookbehind:用于(?<!#)
匹配除# 之外的任何字符,而不捕获它。)
MDN 确实记录了“使用带有全局 /g 标志的 match() 时忽略捕获组”,并建议使用matchAll()
. matchAll() isn't available on Edge or Safari iOS, and you still need to skip the complete match (including the
#`)。
一个更简单的解决方案是切掉前导前缀,如果您知道它的长度 - 在这里,1 表示#
.
const results = ('#foo#bar'.match(/#\w+/g) || []).map(s => s.slice(1));
console.log(results);
[] || ...
如果没有匹配,则该部分是必需的,否则match
返回 null,并且null.map
将不起作用。
const results = ('nothing matches'.match(/#\w+/g) || []).map(s => s.slice(1));
console.log(results);