Javascript: отрицательный эквивалент lookbehind?
есть ли способ достичь эквивалента отрицательный lookbehind в регулярных выражениях javascript? Мне нужно найти строку, которая не начинается с определенного набора символов.
Кажется, я не могу найти регулярное выражение, которое делает это без сбоев, если подобраны часть находится в начале строки. Отрицательные lookbehinds кажутся единственным ответом, но у javascript его нет.
изменить: Это регулярное выражение, которое я хотел бы работать, но это не так:
(?<!([abcdefg]))m
таким образом, он будет соответствовать " m " в " jim " или "m", но не "jam"
11 ответов
Lookbehind Утверждения получил принят на спецификация ECMAScript в 2018 году. Это было реализовано в V8 и поставляется без флагов с Google Chrome v62 и узел.js v6 за флагом и v9 без флага. Итак, если вы разрабатываете среду только для Chrome (например,Электрон), или узел, вы можете начать использовать lookbehinds сегодня!
положительное использование lookbehind:
console.log(
".99 €8.47".match(/(?<=$)\d+(\.\d*)?/) // Matches "9.99"
);
отрицательное использование lookbehind:
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]))
reverse все сопоставленные жетоны
примеры:
я определяю следующие функции:
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-длина look-behind).
поэтому regex в вопрос:
(?<!([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 л, но соответствовал 2-й паре. Но, сопоставляя первые 2 l, а затем игнорируя это совпадение как ложноположительное, механизм regexp исходит из конец этого соответствия и игнорирует любые символы в пределах false положительный.
вы можете определить группу без захвата, отрицая свой набор символов:
(?:[^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 его с .
другое дело, что я не знаю, как он будет обрабатывать более сложные случаи, то есть сложные значения для поддельного 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])
будет соответствовать любой символ не(^
) в
Это может помочь, в зависимости от контекста:
Это соответствует m в jim, но не jam:
"jim jam".replace(/[a-g]m/g, "").match(/m/g)