Javascript Regex - найти все возможные совпадения, даже в уже захваченных матчах
Я пытаюсь получить все возможные совпадения из строки с помощью regex с javascript. Похоже, что мой способ сделать это не соответствует части строки, которые уже были сопоставлены.
Переменные
var string = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y';
var reg = /A[0-9]+B[0-9]+Y:A[0-9]+B[0-9]+Y/g;
код:
var match = string.match(reg);
Все согласованные результаты:
A1B1Y:A1B2Y
A1B5Y:A1B6Y
A1B9Y:A1B10Y
Согласованные результаты, которые я хочу:
A1B1Y:A1B2Y
A1B2Y:A1B3Y
A1B5Y:A1B6Y
A1B6Y:A1B7Y
A1B9Y:A1B10Y
A1B10Y:A1B11Y
В моей голове я хочу, чтобы A1B1Y:A1B2Y
был совпадением с A1B2Y:A1B3Y
, хотя A1B2Y
в строке должен быть частью двух совпадений.
Ответы
Ответ 1
Не изменяя регулярное выражение, вы можете установить его для начала совпадения в начале второй половины матча после каждого матча, используя .exec
и манипулируя регулярным выражением object lastIndex
.
var string = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y';
var reg = /A[0-9]+B[0-9]+Y:A[0-9]+B[0-9]+Y/g;
var matches = [], found;
while (found = reg.exec(string)) {
matches.push(found[0]);
reg.lastIndex -= found[0].split(':')[1].length;
}
console.log(matches);
//["A1B1Y:A1B2Y", "A1B2Y:A1B3Y", "A1B5Y:A1B6Y", "A1B6Y:A1B7Y", "A1B9Y:A1B10Y", "A1B10Y:A1B11Y"]
Демо
Согласно комментарию Берги, вы также можете получить индекс последнего совпадения и увеличить его на 1, чтобы вместо того, чтобы начинать матч со второй половины матча, он начнет пытаться соответствовать второму символу каждый матч далее:
reg.lastIndex = found.index+1;
Демо
Конечный результат тот же. Хотя, обновление Bergi имеет немного меньше кода и быстрее выполняет . =]
Ответ 2
Вы не можете получить прямой результат из match
, но результат можно получить через RegExp.exec
и с некоторой модификацией регулярного выражения:
var regex = /A[0-9]+B[0-9]+Y(?=(:A[0-9]+B[0-9]+Y))/g;
var input = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y'
var arr;
var results = [];
while ((arr = regex.exec(input)) !== null) {
results.push(arr[0] + arr[1]);
}
Я использовал положительный прогноз вперед (?=pattern)
с нулевой шириной, чтобы не потреблять текст, так что перекрывающаяся часть может быть удалена.
На самом деле, можно использовать метод replace
для достижения того же результата:
var input = 'A1B1Y:A1B2Y:A1B3Y:A1B4Z:A1B5Y:A1B6Y:A1B7Y:A1B8Z:A1B9Y:A1B10Y:A1B11Y'
var results = [];
input.replace(/A[0-9]+B[0-9]+Y(?=(:A[0-9]+B[0-9]+Y))/g, function ($0, $1) {
results.push($0 + $1);
return '';
});
Однако, поскольку это replace
, он делает ненужную работу по замене.
Ответ 3
К сожалению, это не так просто, как один string.match
.
Причина в том, что вы хотите совпадающие совпадения, которые не дает вам флаг /g
.
Вы можете использовать lookahead:
var re = /A\d+B\d+Y(?=:A\d+B\d+Y)/g;
Но теперь вы получаете:
string.match(re); // ["A1B1Y", "A1B2Y", "A1B5Y", "A1B6Y", "A1B9Y", "A1B10Y"]
Причина в том, что lookahead имеет нулевую ширину, а это означает, что он просто говорит, следует ли шаблон после того, что вы пытаетесь сопоставить или нет; он не включает его в матч.
Вы можете использовать exec
, чтобы попытаться захватить то, что вы хотите. Если регулярное выражение имеет флаг /g
, вы можете запустить exec
несколько раз, чтобы получить все совпадения:
// using re from above to get the overlapping matches
var m;
var matches = [];
var re2 = /A\d+B\d+Y:A\d+B\d+Y/g; // make another regex to get what we need
while ((m = re.exec(string)) !== null) {
// m is a match object, which has the index of the current match
matches.push(string.substring(m.index).match(re2)[0]);
}
matches == [
"A1B1Y:A1B2Y",
"A1B2Y:A1B3Y",
"A1B5Y:A1B6Y",
"A1B6Y:A1B7Y",
"A1B9Y:A1B10Y",
"A1B10Y:A1B11Y"
];
Вот сценарий этого в действии. Откройте консоль, чтобы увидеть результаты.
В качестве альтернативы вы можете разбить исходную строку на :
, а затем прокрутить результирующий массив, вытащив те, которые соответствуют, когда array[i]
и array[i+1]
совпадают, как вы хотите.