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] совпадают, как вы хотите.