11

我正在编写一组正则表达式来将 CSS 选择器转换为 id 和类数组。

例如,我希望 '#foo#bar' 返回 ['foo', 'bar']。

我一直在努力实现这一目标

"#foo#bar".match(/((?:#)[a-zA-Z0-9\-_]*)/g)

但是当非捕获前缀 ?: 应该忽略 # 字符时,它会返回 ['#foo', '#bar']。

有没有比对每个返回的字符串进行切片更好的解决方案?

4

7 回答 7

12

您可以使用.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]);
于 2012-06-02T18:29:51.097 回答
5

它匹配#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]);
}

看到它在行动

于 2012-06-02T18:19:40.050 回答
2

我不确定你是否可以使用 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'
}
于 2012-06-02T18:32:25.050 回答
1

您可以使用否定的前瞻断言:

"#foo#bar".match(/(?!#)[a-zA-Z0-9\-_]+/g);  // ["foo", "bar"]
于 2012-06-02T18:44:04.037 回答
1

不幸的是,Javascript RegExp 中没有后向断言,否则你可以这样做:

/(?<=#)[a-zA-Z0-9\-_]*/g

除了将它添加到一些新版本的 Javascript 之外,我认为使用split后处理是你最好的选择。

于 2012-06-02T18:31:16.743 回答
1

几年前 mVChr 提到的后向断言被添加到 ECMAScript 2018中。这将允许您这样做:

'#foo#bar'.match(/(?<=#)[a-zA-Z0-9\-_]*/g)(返回["foo", "bar"]

(也可以使用否定的lookbehind:用于(?<!#)匹配除# 之外的任何字符,而不捕获它。)

于 2018-04-19T00:50:55.360 回答
0

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);

于 2019-05-12T21:04:22.017 回答