2009-03-13 1 views
101

Есть ли способ достичь эквивалента negative lookbehind в регулярных выражениях javascript? Мне нужно сопоставить строку, которая не начинается с определенного набора символов.Javascript: отрицательный эквивалент lookbehind?

Кажется, я не могу найти регулярное выражение, которое делает это без сбоев, если совпадающая часть находится в начале строки. Отрицательные lookbehind кажутся единственным ответом, но у javascript его нет.

EDIT: Это регулярное выражение, которое я хотел бы работать, но это не делает:

(?<!([abcdefg]))m

Так что будет соответствовать «м» в «джим» или «м», но не джем "

+0

Рассмотрите возможность размещения регулярных выражений, как это выглядело бы с отрицательным lookbehind; что может облегчить ответ. –

+0

Я добавил комментарий к [ответ, я думаю, имеет самое простое решение вашей проблемы] (http://stackoverflow.com/a/18392742/222134). –

ответ

33

Использование

newString = string.replace(/([abcdefg])?m/, function($0,$1){ return $1?$0:'m';}); 
+9

Это ничего не делает: 'newString' всегда будет равным' string'. Почему так много upvotes? – MikeM

+0

@MikeM: потому что это просто демонстрация метода совпадения. – bug

+50

@ bug. Демонстрация, которая ничего не делает, - это странная демонстрация. Ответ наткнулся, как будто он был просто скопирован и вставлен без какого-либо понимания того, как он работает. Таким образом, отсутствие сопроводительного объяснения и неспособность продемонстрировать, что что-то было согласовано. – MikeM

41

стратегия Mijoja работает для вашего конкретного случая, но не в целом:

js>newString = "Fall ball bill balll llama".replace(/(ba)?ll/g, 
    function($0,$1){ return $1?$0:"[match]";}); 
Fa[match] ball bi[match] balll [match]ama 

Вот пример, когда цель состоит в том, чтобы совместить двойной-l, но не в том случае, если ему предшествует «ba». Обратите внимание на слово «balll» - истинный lookbehind должен был подавить первые 2 l, но соответствовал второй паре. Но, сопоставляя первые 2 l, а затем игнорируя это совпадение как ложное положительное, двигатель регулярного выражения исходит из конца этого совпадения и игнорирует любые символы в ложном положительном.

+5

А, ты прав.Однако это намного ближе, чем раньше. Я могу принять это, пока что-то лучше не появится (например, javascript, фактически реализующий lookbehinds). –

66

Как Javascript поддерживает negative lookahead, один безопасный способ сделать это:

Пусть говорят, что вы хотите сделать с просмотром назад, как этот

(?<!([abcdefg]))m 
  1. Реверс строки, чтобы соответствовать
  2. Применить ваш шаблон «обратный» с использованием lookahead (будьте осторожны с обратным совпадающим выражением внутри lookahead, в этом случае он остается тем же)

    m(?!([abcdefg])) 
    
  3. обратный все совпадающая жетоны

Примеры:

Я определим следующие функции:

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:

Вопрос

следующие @ Андрей.Фил 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: Ø 
+22

проблема с этим подходом заключается в том, что он не работает, когда у вас есть как lookahead, так и lookbehind. – kboom

+2

вы можете показать рабочий пример, скажем, я хочу совместить 'max-height', но не' line-height', и я хочу только совпадение 'height' – neaumusic

+0

Это не помогает, если задача состоит в замене двух последовательных идентичных символов (и не более 2), которым не предшествует какой-либо символ. '' '(?! \() 'заменит апострофы в'' '(' 'test' '' '' '' test' с другого конца, оставив '('' test'NNNtest', а не' ('' testNNN'test'. –

9

Вы могли бы определить, не захватывая группы, отрицая свой набор символов:

(?:[^a-g])m 

... который соответствовал бы каждый mНЕ предшествует любой из этих букв ,

+2

Я думаю, что совпадение также будет охватывать предшествующий символ. – Sam

+3

^this true. Класс символов представляет собой символ ... Вся ваша группа, не связанная с захватом, не делает это значение доступным в контексте замены. Ваше выражение не говорит «каждый m, которому предшествует ни одна из этих букв», он говорит «каждый m *, которому предшествует символ *, который НЕ является ни одной из этих букв» –

+4

Для ответа также разрешите оригинал проблема (начало строки), она также должна включать опцию, поэтому результирующее выражение будет выглядеть следующим образом: '(?: [^ ag] | ^) m'. См. https://regex101.com/r/jL1iW6/2 для запуска Пример: –

-1

/(?![abcdefg])[^abcdefg]m/gi Да, это трюк.

+5

Проверка '(?! [abcdefg])' полностью избыточна, поскольку '[^ abcdefg]' уже выполняет свою работу, чтобы предотвратить совпадение этих символов. – nhahtdh

+2

Это не будет соответствовать «m» без предшествующих символов. –

40

Давайте предположим, что вы хотите, чтобы найти все int не предшествуют unsigned:

С поддержкой негативный взгляд-за:

(?<!unsigned)int 

Без поддержки негативный взгляд-за:

((?!unsigned).{9}|^.{0,8})int 

В основном идея состоит в том, чтобы захватить n предшествующих символов и исключить совпадение с отрицательным внешним видом, но также соответствовать случаям, когда нет предварительной предел n символов. (где n - длина взгляда).

Так что регулярное выражение в вопросе:

(?<!([abcdefg]))m 

бы перевести:

((?!([abcdefg])).|^)m 

Вам может понадобиться, чтобы играть с захватом группы, чтобы найти точное место строки, которая вас интересует или вы хотите замените конкретную часть на что-то другое.

+2

Это должен быть правильный ответ. См. Так: '' Таким образом, он будет соответствовать «m» в «jim» или «m», но не «jam». Replace (/ (j (?! ([Abcdefg])). | ^) M/g , "$ 1 [MATCH]") ' возвращает ' 'Таким образом, он будет соответствовать« m »в« ji [MATCH] »или« m », но не« jam »« Это довольно просто, и это работает! – Asrail

1

Следуя идее Миджоджи, и, опираясь на проблемы, выявленные JasonS, у меня была эта идея; я проверил немного, но я не уверен в себе, так что проверка на кого-то более эксперта, чем у меня в Js регулярное выражение было бы здорово :)

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 в каждой точке в строке между любыми двумя символами, каждый раз, когда эта позиция является отправной точкой:

--- любая подстрока размера, что не хотел (здесь 'ba', таким образом ..) (если размер известен; therwise это должно быть трудно сделать, возможно)

--- --- или меньше, если это начало строки: ^.?

и, после этого,

--- что (здесь 'll').

При каждом вызове checker будет проведен тест, чтобы проверить, не является ли значение до ll (!== 'ba'); если это так, мы вызываем другую функцию, и она должна быть такой (doer), которая будет вносить изменения на str, если целью является это, или более общее, которое будет вводить необходимые данные вручную обработать результаты сканирования str.

здесь мы меняем строку, поэтому нам нужно сохранить след разности длин, чтобы компенсировать местоположения, заданные replace, все рассчитано на str, которое само по себе никогда не изменяется.

поскольку примитивные строки являются неизменными, мы могли бы использовать переменную str для хранения результата всей операции, но я думал, что пример, уже осложнено replacings, будет понятнее, с другой переменной (str_done).

я думаю, что с точкой зрения выступлений он должен быть довольно суровым: все эти бессмысленные заменами «» в «», this str.length-1 раз, плюс здесь ручная замена на делателе, что означает много нарезки ... вероятно, этот конкретный выше случай, который можно сгруппировать, разрезая строку только один раз на части вокруг, где мы хотим вставить [match] и .join(), используя его непосредственно с [match].

Другое дело в том, что я не знаю, как он обрабатывал бы более сложные случаи, то есть сложные значения для поддельного lookbehind ... длина, пожалуй, наиболее проблематичные данные для получения.

и в checker, в случае множественных возможностей нежелательных значений для $ позади, нам нужно будет провести тест на него с еще одним регулярным выражением (для кэширования (создания) вне checker лучше, чтобы избежать тот же объект регулярного выражения, который должен быть создан при каждом вызове для checker), чтобы узнать, не хотим ли мы этого избежать.

надеюсь, что я был чист; если не без колебаний, я постараюсь лучше. :)

0

Это эффективно делает это

"jim".match(/[^a-g]m/) 
> ["im"] 
"jam".match(/[^a-g]m/) 
> null 

поиска и замены пример

"jim jam".replace(/([^a-g])m/g, "$1M") 
> "jiM jam" 

Обратите внимание, что негативный взгляд-за строкой должна быть 1 персонаж долго для этого, чтобы работать.

+0

Не совсем. В «jim» я не хочу «i»; просто «м». И '' m ".match (/ [^ a-g] m /)' yeilds 'null'. Я тоже хочу «m» в этом случае. –

-1

Это может помочь, в зависимости от контекста:

Это соответствует м в джим, но не варенье:

"jim jam".replace(/[a-g]m/g, "").match(/m/g) 
+0

Это работает исключительно для сопоставления, но тогда у меня нет исходной строки для каких-либо манипуляций. Очень близко. –

0

Используя ваш случай, если вы хотите заменитьm с чем-то, например, преобразовать его в верхний регистр M, вы можете отменить набор в группе захвата.

матч ([^a-g])m, заменить $1M

"jim jam".replace(/([^a-g])m/g, "$1M") 
\\jiM jam 

([^a-g]) будет соответствовать любому символу, не (^) в a-g диапазоне, и хранить его в первой группе захвата, так что вы можете получить доступ к нему с $1.

Итак, мы находим im в jim и заменяем его iM, результатом которого является jiM.