Как найти индексы всех несовпадающих символов с помощью регулярного выражения JS?

У меня есть строка, и я хочу получить массив с индексами (позициями) символов в этой строке, которые не соответствуют определенным критериям регулярного выражения.

Проблема здесь в том, что если я напишу это так:

let match;
let reg = /[A-Za-z]|[0-9]/g;
let str = "1111-253-asdasdas";
let indexes = [];

do {
    match = reg.exec(str);
    if (match) indexes.push(match.index);
} while (match);

Оно работает. Он возвращает индексы всех символов, которые являются числовыми или буквенными. Но проблема в том, что если я попытаюсь сделать наоборот, с отрицательным прогнозом в Regex, вот так:

let match;
let reg = /(?!([A-Za-z]|[0-9]))/g;
let str = "1111-253-asdasdas";
let indexes = [];

do {
    match = reg.exec(str);
    if (match) indexes.push(match.index);
} while (match);

Это заканчивается бесконечным циклом.

Я хотел бы добиться того же результата, что и в первом случае, но с отрицательным регулярным выражением, поэтому в этом случае результат будет таким:

indexes = [4, 8]; // which are the indexes in which a non-alphanumerical character appears

Является ли цикл неправильным, или это выражение регулярного выражения тот, кто все испортил? Может быть, exec не работает с отрицательными выражениями Regex?

Я бы понял, что выражение регулярного выражения не работает так, как я хотел (потому что оно может быть неправильно отформатировано), но я не понимаю бесконечный цикл, который заставляет меня думать, что exec, возможно, не лучший способ достичь того, что я находясь в поиске.


person Unapedra    schedule 04.03.2019    source источник
comment
Бесконечный цикл легко объяснить: регулярное выражение имеет модификатор g и, таким образом, пытается сопоставить несколько вхождений шаблона, но поскольку ваш шаблон соответствует пустой строке, и вы не проверяете условие, если index равно lastIndex, регулярное выражение не может продвигаться по строке. Используйте регулярное выражение для соответствия любым небуквенно-цифровым символам, /[\W_]/g   -  person Wiktor Stribiżew    schedule 04.03.2019
comment
вы не можете просто изменить регулярное выражение, чтобы оно искало не буквенно-цифровые символы: /[^A-Za-z0-9]/   -  person Robin Zigmond    schedule 04.03.2019
comment
@WiktorStribiżew спасибо за объяснение, так как это было причиной моего вопроса, потому что я не понимал, почему это происходит.   -  person Unapedra    schedule 04.03.2019
comment
@RobinZigmond спасибо, потому что это именно то, чего я пытался достичь!   -  person Unapedra    schedule 04.03.2019
comment
@WiktorStribiżew Я снова открыл, потому что ваша повторяющаяся ссылка была недостаточно конкретной.   -  person Tim Biegeleisen    schedule 04.03.2019
comment
Извините, @WiktorStribiżew, можете ли вы опубликовать свой комментарий в качестве ответа, чтобы я мог его принять? Это объясняет, почему exec заканчивается бесконечным циклом, и, кроме того, дает рабочий ответ для моего случая, который решает проблему. Благодарю вас!   -  person Unapedra    schedule 04.03.2019
comment
@Unapedra Да, см. ниже.   -  person Wiktor Stribiżew    schedule 04.03.2019


Ответы (2)


Причина

Бесконечный цикл легко объяснить: регулярное выражение имеет модификатор g и, таким образом, пытается сопоставить несколько вхождений шаблона, начиная каждую попытку сопоставления после окончания предыдущего успешного совпадения, то есть после lastIndex значение:

См. exec документацию:

Если в вашем регулярном выражении используется флаг «g», вы можете использовать метод exec() несколько раз, чтобы найти последовательные совпадения в одной и той же строке. При этом поиск начинается с подстроки str, указанной в lastIndex свойство

Однако, поскольку ваш шаблон соответствует пустой строке, и вы не проверяете условие, если индекс равен lastIndex, регулярное выражение не может продвигаться по строке.

Решение

Используйте регулярное выражение для соответствия любым небуквенно-цифровым символам, /[\W_]/g. Поскольку он не соответствует пустым строкам, свойство lastIndex объекта RegExp будет изменяться при каждом совпадении, и бесконечный цикл не возникнет.

Демонстрация JS:

let match, indexes = [];
let reg = /[\W_]/g;
let str = "1111-253-asdasdas";

while (match = reg.exec(str)) {
    indexes.push(match.index);
}
console.log(indexes);

Также см. как вручную переместить значение свойства lastIndex.

person Wiktor Stribiżew    schedule 04.03.2019

Этот подход заменяет все совпадающие символы звездочкой *. Затем мы повторяем эту замененную строку и извлекаем все индексы, которые не соответствуют классу символов регулярного выражения.

var str = "1111-253-asdasdas";
var pattern = /[^A-Za-z0-9]/g;
str = str.replace(pattern, "*");

var indices = [];
for(var i=0; i < str.length;i++) {
    if (str[i] === "*") indices.push(i);
}
console.log(indices.toString());

В этом случае не совпадают только символы в позициях 4 и 8, потому что они являются символами подчеркивания.

person Tim Biegeleisen    schedule 04.03.2019
comment
Спасибо за ответ, но вы имеете в виду тире/дефисы, а не символы подчеркивания? - person Crashalot; 24.06.2020