MatchAll: примеры (JAVASCRIPT)
matchAll(regexp): iteratorБазовая информация о методе matchAll()
Метод matchAll() применяется к строке и возвращает итератор по всем результатам сопоставления этой строки с указанным регулярным выражением. Каждый результат представляет собой массив с дополнительными свойствами index и input.
Чаще всего его используют, когда необходимо получить не просто первое совпадение, а все группы захвата из каждого совпадения глобального регулярного выражения.
Единственный аргумент метода — объект регулярного выражения. Если передается строка, она автоматически преобразуется в RegExp. Важное требование: регулярное выражение должно иметь глобальный флаг g, иначе будет выброшена ошибка TypeError.
Возвращаемое значение — итератор (не массив). Каждый элемент итератора — массив, содержащий полное совпадение в виде первого элемента, а затем все группы захвата. Дополнительно каждый массив имеет свойства index (позиция совпадения) и input (исходная строка). Для работы с результатами как с массивом итератор можно преобразовать, используя Array.from() или оператор распространения (...).
Простые примеры использования
Поиск всех слов, начинающихся с заглавной буквы:
const text = 'JavaScript и Python — популярные языки.';
const regex = /\b[A-ZА-Я][a-zа-я]*\b/g;
const matches = [...text.matchAll(regex)];
console.log(matches.map(m => m[0]));['JavaScript', 'Python']
Использование с группами захвата для извлечения данных:
const data = '10px, 20em, 30rem';
const regex = /(\d+)(px|em|rem)/g;
for (const match of data.matchAll(regex)) {
console.log(`Значение: ${match[1]}, Единица: ${match[2]}, Позиция: ${match.index}`);
}Значение: 10, Единица: px, Позиция: 0 Значение: 20, Единица: em, Позиция: 5 Значение: 30, Единица: rem, Позиция: 11
Похожие методы в JavaScript
String.match() возвращает массив совпадений, но без детальной информации о позициях и группах для каждого совпадения при глобальном поиске. Метод matchAll предпочтительнее, когда нужны группы захвата из всех совпадений.
RegExp.exec() в цикле дает схожий результат, но требует ручного управления. Метод matchAll является более современным и удобным способом.
String.split() с регулярным выражением может разделять строку, но не предназначен для извлечения групп.
String.search() только находит позицию первого совпадения.
Аналоги в других языках программирования
Python: Модуль re предоставляет методы finditer() и findall(). finditer() возвращает итератор объектов match, что наиболее близко к matchAll.
import re
text = '10px, 20em, 30rem'
pattern = re.compile(r'(\d+)(px|em|rem)')
for match in pattern.finditer(text):
print(match.groups(), match.start())('10', 'px') 0
('20', 'em') 5
('30', 'rem') 11PHP: Функция preg_match_all() возвращает количество совпадений и заполняет массив результатами, включая группы.
$text = '10px, 20em, 30rem';
preg_match_all('/(\d+)(px|em|rem)/', $text, $matches, PREG_SET_ORDER);
print_r($matches);Array ( [0] => Array ( [0] => 10px [1] => 10 [2] => px ) [1] => Array ... )
C#: Метод Matches() класса Regex возвращает коллекцию объектов Match, содержащих группы.
В отличие от JavaScript, во многих языках (Python, PHP) глобальный флаг не нужно явно указывать для поиска всех совпадений.
Распространенные ошибки
Передача регулярного выражения без флага g приводит к исключению.
const text = 'test';
try {
[...text.matchAll(/test/)]; // Нет флага g
} catch (e) {
console.log(e.name); // TypeError
}TypeError
Попытка повторного использования итератора. Итератор, возвращаемый matchAll, является одноразовым.
const iterator = 'ab'.matchAll(/a/g);
console.log([...iterator]); // Первый проход
console.log([...iterator]); // Второй проход - пусто[Array ['a']] []
Неправильное преобразование результата. Итератор напрямую не является массивом.
const result = '12'.matchAll(/\d/g);
console.log(result[0]); // undefined
// Правильно:
const array = [...result];
console.log(array[0][0]); // '1'undefined 1
Изменения в спецификации
Метод String.prototype.matchAll был добавлен в стандарт ECMAScript 2020 (ES11). До его появления для получения аналогичного результата необходимо было использовать цикл с RegExp.exec(). Некоторые современные движки JavaScript начали поддерживать этот метод раньше официального принятия стандарта.
Расширенные примеры применения
Парсинг сложных шаблонов с именованными группами захвата:
const log = 'ERROR [2023-10-05] File not found\nWARN [2023-10-06] Deprecated API';
const regex = /(?\w+)\s\[(?[^\]]+)\]\s(?.+)/g;
const logs = [];
for (const {groups} of log.matchAll(regex)) {
logs.push(groups);
}
console.log(logs); [
{ level: 'ERROR', date: '2023-10-05', message: 'File not found' },
{ level: 'WARN', date: '2023-10-06', message: 'Deprecated API' }
]Поиск пересекающихся совпадений с использованием lookahead:
const str = 'abc';
const regex = /(?=(.{2}))/g; // Ищем все позиции, за которыми следует 2 символа
const matches = [...str.matchAll(regex)].map(m => m[1]);
console.log(matches); // 'ab', 'bc'['ab', 'bc']
Обработка многострочного текста с флагами g и m (multiline):
const multilineText = `Строка 1
Строка 2
Строка 3`;
const regex = /^Строка\s(\d+)/gm;
const lines = [];
for (const match of multilineText.matchAll(regex)) {
lines.push(`Номер строки в тексте: ${match[1]}, позиция: ${match.index}`);
}
console.log(lines);['Номер строки в тексте: 1, позиция: 0', 'Номер строки в тексте: 2, позиция: 9', 'Номер строки в тексте: 3, позиция: 18']
Использование matchAll вместе с другими методами массивов для сложной фильтрации:
const code = 'let x=5; let y=10; const z=15;';
const varRegex = /(let|const)\s+(\w+)\s*=/g;
const variables = [...code.matchAll(varRegex)]
.filter(match => match[1] === 'let')
.map(match => match[2]);
console.log(variables); // Только переменные, объявленные через let['x', 'y']