Учитывая две строки (обозначаемые A и B) и набор N строк, мне нужно написать регулярное выражение, чтобы проверить, содержит ли данная входная строка W подстроку S, где S — любая подстрока, которая удовлетворяет всем следующим трем условиям. : 1. начинается с буквы А; 2. заканчивается на B; 3. ни один элемент N не встречается в части между А и В (эта часть не пересекается с А и В).
Например, я выбрал "ab" в качестве A, "bc" в качестве B, ["a", "cb", "cd"] в качестве N. Если "ec" — внутренняя часть, то "abecbc" — это строка, удовлетворяющая всем трем условиям: если W содержит такую подстроку, регулярное выражение должно вернуть true. Мой первый вариант — это следующее регулярное выражение:
var T = /(?=ab.*bc)(?=(?!ab.*a.*bc))(?=(?!ab.*cb.*bc))(?=(?!ab.*cd.*bc))/;
Я выбрал W = S = "abecbc". Это регулярное выражение работает так, как ожидалось:
T.test("abecbc");
// true
Но меня интересует следующая проблема: как написать функционально эквивалентное регулярное выражение, не используя положительный просмотр (?=)в качестве оператора AND?
Итак, мой второй вариант следующий:
var R = /ab(?!.*?(?:a|cb|cd).*)bc/;
Но R.test("abecbc") оценивается как false. Итак, разделим R на три части:
/ab(.*)/.test("abecbc")
возвращается true. Затем
/(.*)bc/.test("abecbc")
возвращается true.
Внутренняя часть (т. е. часть между "ab" и "bc") — это "ec". И
/(?!.*?(?:a|cb|cd).*)/.test("ec")
возвращает true, что и ожидалось. Значит, истин должно быть три, и частей в R больше нет. Тогда почему
/ab(?!.*?(?:a|cb|cd).*)bc/.test("abecbc")
оценить на false? И как написать правильное регулярное выражение, решающее проблему, описанную в первом абзаце поста, не используя положительный просмотр (?=)в качестве оператора AND?
РЕДАКТИРОВАТЬ
Мой вопрос не является дубликатом этого вопроса: мне нужно объяснение, почему конкретное регулярное выражение (R) возвращает false вместо true. Еще одно отличие состоит в том, что мне не нужно проверять, содержит ли внутренняя часть указанную строку.
@WiktorStribiżew: нет необходимости проверять ec посередине. А в вашем регулярном выражении я не понимаю назначения b?.
Как объяснить, почему R возвращается false?
Если вам не нужно проверять наличие ec внутри, это еще проще, ab(?!(?:a|cb|cd|ab|bc).)*bc. Вам необходимо включить начальный и конечный разделители в просмотр вперед. Все еще дубликат, вам просто нужен закаленный жадный жетон
@WiktorStribiżew Помимо отсутствующей внешней группы в вашем регулярном выражении, использование смягченного жадного токена приведет к ложноотрицательным результатам, когда то, что находится между A и B, соответствует одному из N путем перекрытия с B, что нарушает правило ОП «эта часть не перекрывается с A». и Б». Например, abcbc должен совпадать, потому что c не является ни одним из a, cb и cd, но при вашем подходе он не будет совпадать, потому что cb пересекается с bc. Демо: regex101.com/r/NqLbfV/5
@blhsing Здесь нет необходимости в какой-либо внешней группе. И ваше регулярное выражение в ответе дает те же три совпадения, что и регулярное выражение ab(?:(?!a|cb|cd|ab|bc).)*bc.
@WiktorStribiżew К сожалению, я опубликовал не ту демо-версию. Отсутствуя внешнюю группу, я имел в виду опубликованное вами регулярное выражение ab(?!(?:a|cb|cd|ab|bc).)*bc, которое вы теперь исправили, правильно сделав группу без захвата внешней группой. Демонстрация указанного ложноотрицательного результата с вашим регулярным выражением: regex101.com/r/M3EgpH/1 И демонстрация сопоставления того же ввода с моим регулярным выражением: regex101.com/r/M3EgpH/2
Я не понимаю роль |ab|bc в ab(?:(?!a|cb|cd|ab|bc).)*bc.
@lyricalwicked Включение |ab|bc в отрицательный просмотр вперед предотвращает появление A и B между A и B . Это еще один способ провести нежадное сопоставление.
@blhsing: Но я не упомянул, что мне нужно нежадное совпадение: на самом деле жадное совпадение было бы предпочтительнее. И "0abbc1ab2bc3ababc4abcbbc5ab6bc7".match(/ab(?:(?!a|cb|cd).)*bc/g) дает те же три совпадения, значит, это регулярное выражение тоже решает проблему?
@lyricallywicked Верно, но опять же, если вы не хотите соблюдать свое правило «эта часть не пересекается с A и B». Демонстрация вашего жадного регулярного выражения, не соответствующего моему образцу ввода abcbc: regex101.com/r/M3EgpH/3



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Ваша попытка регулярного выражения R = /ab(?!.*?(?:a|cb|cd).*)bc/ не соответствует abecbc, потому что шаблон отрицательного просмотра представляет собой утверждение нулевой ширины, поэтому за вашим регулярным выражением bc должно немедленно следовать ab. И если вы попытаетесь исправить это, добавив .* перед bc, то нет никакой гарантии, что совпадение a|cb|cd произойдет между ab и bc.
Вместо этого вы можете захватить B и то, что следует за ним, чтобы вы могли использовать захват в качестве окончания в утверждении отрицательного просмотра вперед, чтобы избежать совпадения, когда между A и B есть какое-либо из N:
ab(?=.*?(bc.*))(?!.*(?:a|cb|cd).*\1).*?bc
Демо: https://regex101.com/r/NqLbfV/4
Как написать регулярное выражение R такое, чтобы, например, "0abbc1ab2bc3ababc4abcbbc5ab6bc7".match(R) возвращало ["abbc", "ab2bc", "ab6bc"]? Я использовал вариант в ответе как /(ab(?=.*(bc.*))(?!.*(?:a|cb|cd).*\1))/g, но он возвращает [ "ab" ].
Вы можете просто включить .*?bc в шаблон. Тогда я обновил ответ соответственно.
Если возможно, добавьте более подробное объяснение R: я не понимаю, почему он «по сути соответствует только abcd, пока нет a, cb и cd после ab».
Тогда я перефразировал свои объяснения. Надеюсь, теперь стало яснее.