Ответ 1
JavaScript не поддерживает lookbehinds, например (?<=…)
(положительный) и (?<!…)
(отрицательный), но это не значит, что вы еще не можете реализовать это своего рода логика в JavaScript.
Соответствие (не глобальное)
Положительное соответствие lookbehind:
// from /(?<=foo)bar/i
var matcher = mystring.match( /foo(bar)/i );
if (matcher) {
// do stuff with matcher[1] which is the part that matches "bar"
}
Фиксированная ширина отрицательного соответствия lookbehind:
// from /(?<!foo)bar/i
var matcher = mystring.match( /(?!foo)(?:^.{0,2}|.{3})(bar)/i );
if (matcher) {
// do stuff with matcher[1] ("bar"), knowing that it does not follow "foo"
}
Отрицательные lookbehind могут быть выполнены без глобального флага, но только с фиксированной шириной, и вам нужно рассчитать эту ширину (что может затрудниться с чередования). Использование (?!foo).{3}(bar)
было бы более простым и примерно эквивалентным, но оно не будет соответствовать строке, начинающейся с "арматуры", поскольку .
не может соответствовать символам новой строки, поэтому нам нужно, чтобы чередование этого кода было чередованием строк соответствия с "баром" перед символом четыре.
Если вам нужна переменная ширина, используйте нижнее глобальное решение и поставьте break
в конце строфы if
. (Это ограничение довольно распространено. . NET, vim и JGsoft являются only regex, которые поддерживают переменную ширину. PCRE, PHP, а Perl ограниченный фиксированной шириной. Python требует альтернативный регулярный модуль, чтобы поддержать это. Таким образом, логика обходного пути ниже должна работать для всех языков, поддерживающих регулярное выражение.)
Соответствие (глобальное)
Если вам нужно зацикливать на каждом совпадении в заданной строке (модификатор g
, глобальное сопоставление), вы должны переопределить переменную matcher
в каждой итерации цикла, и вы должны использовать RegExp.exec()
(с RegExp создан перед циклом), потому что String.match()
интерпретирует глобальный модификатор иначе и создаст бесконечный цикл!
Глобальный положительный lookbehind:
var re = /foo(bar)/gi; // from /(?<=foo)bar/gi
while ( matcher = re.exec(mystring) ) {
// do stuff with matcher[1] which is the part that matches "bar"
}
"Stuff" может, конечно, включать заполнение массива для дальнейшего использования.
Глобальный негативный вид:
var re = /(foo)?bar/gi; // from /(?<!foo)bar/gi
while ( matcher = re.exec(mystring) ) {
if (!matcher[1]) {
// do stuff with matcher[0] ("bar"), knowing that it does not follow "foo"
}
}
Обратите внимание, что есть случаи, в которых это не будет полностью отражать отрицательный lookbehind. Рассмотрим /(?<!ba)ll/g
совпадение с Fall ball bill balll llama
. Он найдет только три из четырех желаемых совпадений, потому что, когда он анализирует balll
, он находит ball
, а затем продолжает один символ в конце с l llama
. Это происходит только тогда, когда частичное совпадение в конце может помешать частичному совпадению на другом конце (balll
breaks (ba)?ll
, но foobarbar
в порядке с (foo)?bar
). Единственным решением для этого является использование вышеуказанного фиксированного метод ширины.
Замена
Там есть замечательная статья под названием Mimicking Lookbehind в JavaScript, которая описывает, как это сделать.
У него даже есть продолжение, которое указывает на набор коротких функций, которые реализуют это в JS.
Реализация lookbehind в String.replace()
намного проще, поскольку вы можете создать анонимную функцию
Отрицательная замена lookbehind:
// assuming you wanted mystring.replace(/(?<!foo)bar/i, "baz"):
mystring = mystring.replace( /(foo)?bar/i,
function ($0, $1) { return ($1 ? $0 : "baz") }
);