Учитывая две строки (обозначаемые 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
Ваша попытка регулярного выражения 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
».
Тогда я перефразировал свои объяснения. Надеюсь, теперь стало яснее.