Javascript: отрицательный эквивалент lookbehind?
есть ли способ достичь эквивалента a отрицательный lookbehind в регулярных выражениях javascript? Мне нужно найти строку, которая не начинается с определенного набора символов.
Кажется, я не могу найти регулярное выражение, которое делает это без сбоев, если подобраны часть находится в начале строки. Негативные lookbehinds, похоже, единственный ответ, но в JavaScript нет.
изменить:
Это регулярное выражение, которое я хотел бы работать, но это не так:
(?<!([abcdefg]))m
Так что это будет соответствовать "м" в "Джим" или "М", но не "джем"
11 ответов:
Утверждения Касательно Предшествующего Текста получил принят на спецификация ECMAScript в 2018 году. Это было реализовано в V8 и поставляется без флагов с Google Chrome v62 и узел.js v6 за флагом и v9 без флага. Итак, если вы разрабатываете только для Chrome среды (например,Электрон), или узел, вы можете начать использовать lookbehinds сегодня же!
положительное использование lookbehind:
console.log( ".99 €8.47".match(/(?<=$)\d+(\.\d*)?/) // Matches "9.99" );использование отрицательного просмотра назад :
console.log( ".99 €8.47".match(/(?<!$)\d+(?:\.\d*)/) // Matches "8.47" );поддержка на других платформах:
- Mozilla Firefox работает над этим: отслеживается здесь.
- Microsoft Edge тоже работает над этим: отслеживается здесь (голос пользователя предложение).
как Javascript поддерживает отрицательный lookahead, один безопасный способ сделать это:
Допустим, вы хотите сделать lookbehind, как это
(?<!([abcdefg]))m
- переверните строку, чтобы соответствовать
примените свой шаблон "reversed" с помощью lookahead (будьте осторожны с обратным выражением соответствия внутри lookahead, в этом случае он остается прежним)
m(?!([abcdefg]))реверсировать все совпадения жетоны
примеры:
я определяю следующие функции:
const reverse = s => s.split('').reverse().join(''); const test = (stringToTests, reversedRegexp) => stringToTests .map(reverse) .forEach((s,i) => { const match = reversedRegexp.test(s); console.log( stringToTests[i], match, 'token:', match ? reverse(reversedRegexp.exec(s)[0]) : 'Ø' ); });Пример 1:
следующий вопрос @andrew-ensley:
test(['jim', 'm', 'jam'], /m(?!([abcdefg]))/)выходы:
jim true token: m m true token: m jam false token: ØПример 2:
после @ neaumusic комментарий (матч
max-heightа неline-heightмаркер будучиheight):test(['max-height', 'line-height'], /thgieh(?!(-enil))/)выходы:
max-height true token: height line-height false token: Ø
предположим, вы хотите найти все
intне предшествуетunsigned:С поддержкой негативного взгляда назад:
(?<!unsigned )intбез поддержки негативного взгляда назад:
((?!unsigned ).{9}|^.{0,8})intв основном идея состоит в том, чтобы захватить n предыдущих символов и исключить совпадение с отрицательным прогнозом, но также сопоставить случаи, когда нет предшествующих N символов. (где n-длина взгляда назад).
так что регулярное выражение в вопрос:
(?<!([abcdefg]))mперевести на:
((?!([abcdefg])).|^)mвозможно, вам придется играть с группами захвата, чтобы найти точное место строки, которая вас интересует, или вы хотите заменить определенную часть чем-то другим.
стратегия Mijoja работает для вашего конкретного случая, но не в целом:
js>newString = "Fall ball bill balll llama".replace(/(ba)?ll/g, function(,){ return ?:"[match]";}); Fa[match] ball bi[match] balll [match]amaвот пример, где цель состоит в том, чтобы соответствовать двойной-l, но не если ему предшествует "ba". Обратите внимание на слово "balll" -- true lookbehind должен был подавить первые 2 l, но соответствовал 2-й паре. Но, сопоставляя первые 2 l, а затем игнорируя это совпадение как ложное положительное, механизм регулярных выражений исходит из конец этого совпадения и игнорирует любые символы в ложном положительный.
вы можете определить группу без захвата, отрицая свой набор символов:
(?:[^a-g])m...который будет соответствовать каждому
mне перед любым из этих писем.
следуя идее Mijoja и опираясь на проблемы, выявленные JasonS, у меня была эта идея; я немного проверил, но не уверен в себе, поэтому проверка кем-то более опытным, чем я, в JS regex была бы отличной :)
var re = /(?=(..|^.?)(ll))/g // matches empty string position // whenever this position is followed by // a string of length equal or inferior (in case of "^") // to "lookbehind" value // + actual value we would want to match , str = "Fall ball bill balll llama" , str_done = str , len_difference = 0 , doer = function (where_in_str, to_replace) { str_done = str_done.slice(0, where_in_str + len_difference) + "[match]" + str_done.slice(where_in_str + len_difference + to_replace.length) len_difference = str_done.length - str.length /* if str smaller: len_difference will be positive else will be negative */ } /* the actual function that would do whatever we want to do with the matches; this above is only an example from Jason's */ /* function input of .replace(), only there to test the value of $behind and if negative, call doer() with interesting parameters */ , checker = function ($match, $behind, $after, $where, $str) { if ($behind !== "ba") doer ( $where + $behind.length , $after /* one will choose the interesting arguments to give to the doer, it's only an example */ ) return $match // empty string anyhow, but well } str.replace(re, checker) console.log(str_done)мой личный вывод:
Fa[match] ball bi[match] bal[match] [match]amaпринцип заключается в вызове
checkerв каждой точке строки между любыми двумя символами, когда эта позиция является начальной точкой:- - - любая подстрока размера то, что не нужно (здесь ,
..) (если этот размер известен; в противном случае это должно быть труднее сделать, возможно)--- --- или меньше, если это начало строки:
^.?и после этого
--- что на самом деле искал (здесь
'll').при каждом вызове
checker, там будет тест, чтобы проверить, если значение передllэто не то, чего мы не хотим (!== 'ba'); если это так, мы называем другого функция, и она должна быть именно этой (doer), который внесет изменения на str, если цель эта, или более обобщенно, что позволит получить на входе необходимые данные для ручной обработки результатов сканированияstr.здесь мы меняем строку, поэтому нам нужно было отслеживать разницу длины, чтобы компенсировать местоположения, заданные
replace, все рассчитано наstr, который сам никогда не меняется.начиная с примитивных строк являются неизменяемыми, мы могли бы использовать переменную
strчтобы сохранить результат всей операции, но я думал, что пример, уже осложненный заменами, будет более ясным с другой переменной (str_done).я думаю, что в плане выступлений это должно быть довольно жестким: все эти бессмысленные замены " в ",
this str.length-1раз, плюс здесь ручная замена на doer, что означает много нарезки... вероятно, в данном конкретном случае выше, которые могут быть сгруппированы по разрезание строки только один раз на куски вокруг, где мы хотим вставить[match]и.join()ing его с[match]сам по себе.другое дело, что я не знаю, как он будет обрабатывать более сложные случаи, то есть сложные значения для поддельного lookbehind... длина, пожалуй, самые проблемные данные, чтобы получить.
и
checker, в случае нескольких возможностей неназванных значений для $behind, нам придется сделать тест на нем с еще одним регулярным выражением (чтобы быть кэшируется (создается) внеcheckerлучше всего, чтобы избежать того же объекта regex, который будет создан при каждом вызовеchecker), чтобы знать, действительно ли это то, что мы стремимся избежать.надеюсь, что я был ясен; если нет, не стесняйтесь, я постараюсь лучше. :)
это эффективно делает это
"jim".match(/[^a-g]m/) > ["im"] "jam".match(/[^a-g]m/) > nullпоиск и замена пример
"jim jam".replace(/([^a-g])m/g, "M") > "jiM jam"обратите внимание, что отрицательная строка поиска должна быть длиной 1 символ, чтобы это работало.
через ваши дела, если вы хотите заменить
mС чем-то, например, преобразовать его в верхний регистрM, вы можете отменить набор в группе захвата.матч
([^a-g])mзаменить наM"jim jam".replace(/([^a-g])m/g, "M") \jiM jam
([^a-g])будет соответствовать любой символ не(^) вa-gдиапазон, и хранить его в первой группе захвата, так что вы можете получить к нему доступ с.Итак, мы видим
imнаjimи заменитьiMчто приводит кjiM.
Это может помочь, в зависимости от контекста:
Это соответствует m в jim, но не jam:
"jim jam".replace(/[a-g]m/g, "").match(/m/g)
Comments