Javascript RegExp не захватывающие группы
Я пишу набор RegExps для перевода селектора CSS в массивы идентификаторов и классов.
Например, мне бы хотелось, чтобы '# foo # bar' возвращался ['foo', 'bar'].
Я пытался добиться этого с помощью
"#foo#bar".match(/((?:#)[a-zA-Z0-9\-_]*)/g)
но он возвращает ['#foo', '#bar'], когда префикс без захвата?: должен игнорировать символ #.
Есть ли лучшее решение, чем нарезать каждую из возвращаемых строк?
Ответы
Ответ 1
Вы можете использовать .replace()
или .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]);
Ответ 2
Он соответствует #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]);
}
Посмотрите на действие.
Ответ 3
Я не уверен, что вы можете сделать это с помощью 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'
}
Ответ 4
К сожалению, в Javascript RegExp нет утверждения lookbehind, иначе вы могли бы сделать это:
/(?<=#)[a-zA-Z0-9\-_]*/g
Кроме того, что он добавляется в какую-то новую версию Javascript, я думаю, что использование пост-обработки split
- ваш лучший выбор.
Ответ 5
Вы можете использовать отрицательное утверждение:
"#foo#bar".match(/(?!#)[a-zA-Z0-9\-_]+/g); // ["foo", "bar"]
Ответ 6
Утверждение за кадром, упомянутое несколько лет назад mVChr, добавлено в ECMAScript 2018. Это позволит вам сделать это:
'#foo#bar'.match(/(?<=#)[a-zA-Z0-9\-_]*/g)
(возвращает ["foo", "bar"]
)
(Возможен также отрицательный взгляд назад: используйте (?<!#)
Чтобы сопоставить любой символ, кроме #, без его захвата.)
Ответ 7
MDN документирует, что "группы захвата игнорируются при использовании match() с глобальным флагом /g", и рекомендует использовать 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);