Регулярные выражения JavaScript и вспомогательные матчи
Почему суб-совпадения Javascript перестают работать, если установлен модификатор g
?
var text = 'test test test test';
var result = text.match(/t(e)(s)t/);
// Result: ["test", "e", "s"]
Вышеприведенное работает нормально, result[1]
равен "e"
а result[2]
- "s"
.
var result = text.match(/t(e)(s)t/g);
// Result: ["test", "test", "test", "test"]
Вышесказанное игнорирует мои группы захвата. Является ли следующее единственно верным решением?
var result = text.match(/test/g);
for (var i in result) {
console.log(result[i].match(/t(e)(s)t/));
}
/* Result:
["test", "e", "s"]
["test", "e", "s"]
["test", "e", "s"]
["test", "e", "s"]
*/
РЕДАКТИРОВАТЬ:
Я снова вернулся, чтобы с радостью сказать вам, что через 10 лет вы можете сделать это (.matchAll был добавлен в спецификацию)
let result = [...text.matchAll(/t(e)(s)t/g)];
Ответы
Ответ 1
Я удивлен, увидев, что я первый, кто ответил на этот вопрос ответом, который искал 10 лет назад (ответа еще не было). Я также надеялся, что настоящие авторы спецификаций ответят на это раньше меня;).
.matchAll уже добавлен в несколько браузеров.
В современном javascript мы можем сделать это, просто выполнив следующее.
let result = [...text.matchAll(/t(e)(s)t/g)];
.matchAll spec
.matchВсе документы
Теперь я поддерживаю изоморфную библиотеку javascript, которая помогает во многих видах синтаксического анализа строк. Вы можете проверить это здесь: string-saw. Это помогает упростить использование .matchAll при использовании именованных групп захвата.
Ответ 2
Использование функции String
match()
не будет возвращать захваченные группы, если глобальный модификатор установлен, как вы выяснили.
В этом случае вы захотите использовать объект RegExp
и вызвать его функцию exec()
. String
match()
почти идентичен функции RegExp
exec()
... кроме случаев, подобных этим. Если глобальный модификатор установлен, нормальная функция match()
не возвращает возвращенные группы, а функция RegExp
exec()
будет. (Отмечено здесь, среди других мест.)
Еще один улов, который следует помнить, заключается в том, что exec()
не возвращает совпадений в одном большом массиве - он продолжает возвращать совпадения до тех пор, пока не закончится, и в этом случае он возвращает null
.
Итак, например, вы можете сделать что-то вроде этого:
var pattern = /t(e)(s)t/g; // Alternatively, "new RegExp('t(e)(s)t', 'g');"
var match;
while (match = pattern.exec(text)) {
// Do something with the match (["test", "e", "s"]) here...
}
Еще одно замечание: RegExp.prototype.exec()
и RegExp.prototype.test()
выполняют регулярное выражение в предоставленной строке и возвращают первый результат. Каждый последовательный вызов будет проходить через обновление набора результатов RegExp.prototype.lastIndex
на основе текущей позиции в строке.
Вот пример: // помним, что в примере и шаблоне есть 4 совпадения. lastIndex начинается с 0
pattern.test(text); // pattern.lastIndex = 4
pattern.test(text); // pattern.lastIndex = 9
pattern.exec(text); // pattern.lastIndex = 14
pattern.exec(text); // pattern.lastIndex = 19
// if we were to call pattern.exec(text) again it would return null and reset the pattern.lastIndex to 0
while (var match = pattern.exec(text)) {
// never gets run because we already traversed the string
console.log(match);
}
pattern.test(text); // pattern.lastIndex = 4
pattern.test(text); // pattern.lastIndex = 9
// however we can reset the lastIndex and it will give us the ability to traverse the string from the start again or any specific position in the string
pattern.lastIndex = 0;
while (var match = pattern.exec(text)) {
// outputs all matches
console.log(match);
}
Вы можете найти информацию о том, как использовать RegExp
объекты в MDN (в частности, здесь документация для функция exec()
).