Javascript: Negatif gözetleme eşdeğeri?


142

Javascript düzenli ifadelerinde olumsuz bir bakış açısına eşdeğer bir yol elde etmenin bir yolu var mı ? Belirli bir karakter kümesi ile başlamayan bir dize eşleşmesi gerekiyor.

Eşleşen bölüm dizenin başında bulunursa, başarısız olmadan bunu yapan bir normal ifade bulamıyorum görünüyor. Olumsuz bakışlar tek cevap gibi görünüyor, ancak javascript'in bir cevabı yok.

EDIT: Bu ben çalışmak istiyorum regex, ama değil:

(?<!([abcdefg]))m

Yani 'jim' veya 'm' içindeki 'm' ile eşleşir, ancak 'jam' ile eşleşmez


Normal ifadeyi olumsuz bir bakışla göründüğü gibi yayınlamayı düşünün; yanıt vermeyi kolaylaştırabilir.
Daniel LeCheminant


@ WiktorStribiżew: 2018 teknik özelliklerine bakışlar eklendi. Chrome onları destekliyor, ancak Firefox hala spesifikasyonu uygulamadı .
Lonnie Best

Bunun arkasına bir bakmaya ihtiyacı var mı? Ne olmuş (?:[^abcdefg]|^)(m)? Olduğu gibi"mango".match(/(?:[^abcdefg]|^)(m)/)[1]
slebetman

Yanıtlar:


58

Geriye İlerleme İddialar var kabul içine ECMAScript şartnamede 2018 yılında.

Kullanımda olumlu görünüm:

console.log(
  "$9.99  €8.47".match(/(?<=\$)\d+(\.\d*)?/) // Matches "9.99"
);

Kullanımdaki olumsuz görünüm:

console.log(
  "$9.99  €8.47".match(/(?<!\$)\d+(?:\.\d*)/) // Matches "8.47"
);

Platform desteği:


2
çoklu dolgu var mı?
Killy

1
@Killy bildiğim kadarıyla yok ve bir tane oluşturmak potansiyel olarak çok pratik olmayacağından şüpheleniyorum (IE JS'de tam bir Regex uygulaması yazıyor)
Okku

Bir babel eklentisi kullanmaya ne dersiniz, ES5'e derlenmiş veya zaten desteklenen ES6'ya sahip olmak mümkün mü?
Stefan J

1
@IlpoOksanen Bence RegEx uygulamasını genişletmek demek ... çoklu dolgular ne yapar .... ve JavaScript mantık yazmakla yanlış bir şey yok
neaumusic

1
Neden bahsediyorsun? Neredeyse tüm teklifler diğer dillerden esinlenilmiştir ve her zaman idiomatic JS ve geriye dönük uyumluluk bağlamında mantıklı olduğu diğer dillerin sözdizimi ve anlambilimiyle eşleşmeyi tercih edecektir. 2017'de 2018 spesifikasyonuna hem olumlu hem de olumsuz bakışların kabul edildiğini açıkça belirtmiştim ve kaynaklara bağlantılar verdim. Ayrıca, söz konusu spesifikasyonu hangi platformların uyguladığını ve diğer platformların durumunun ne olduğunu ayrıntılı olarak anlattım - ve o zamandan beri güncelliyoruz. Doğal olarak bu göreceğimiz son
Regexp

83

2018'den bu yana Lookbehind Assertions , ECMAScript dil spesifikasyonunun bir parçasıdır .

// positive lookbehind
(?<=...)
// negative lookbehind
(?<!...)

Cevap 2018 öncesi

Javascript negatif ileriye bakmayı desteklediğinden , bunu yapmanın bir yolu:

  1. giriş dizesini ters çevir

  2. ters regex ile eşleştir

  3. maçları tersine çevir ve yeniden biçimlendir


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]) : 'Ø');
  });

Örnek 1:

@ Andrew-ensley'nin sorusu şu:

test(['jim', 'm', 'jam'], /m(?!([abcdefg]))/)

Çıktılar:

jim true token: m
m true token: m
jam false token: Ø

Örnek 2:

@Neaumusic yorumunu (maç sonrasında max-heightancak line-height, simge varlık height):

test(['max-height', 'line-height'], /thgieh(?!(-enil))/)

Çıktılar:

max-height true token: height
line-height false token: Ø

36
Bu yaklaşımla ilgili sorun, hem ileri geri hem de geriye
baktığınızda işe yaramaz

3
Lütfen çalışan bir örnek gösterebilir misin, maç yapmak istiyorum max-heightama değil line-heightve sadece maçın olmasını istiyorumheight
neaumusic

Görevin önünde bazı semboller olmayan iki ardışık özdeş sembolü (ve en fazla 2) değiştirmek yardımcı olmaz. ''(?!\()kesme işaretlerini yerini alacak ''(''test'''''''testböylece bırakarak diğer ucundan (''test'NNNtestziyade (''testNNN'test.
Wiktor Stribiżew

61

intÖncelikle öncesinde olmayan her şeyi bulmak istediğinizi varsayalım unsigned:

Olumsuz bakış desteği ile:

(?<!unsigned )int

Olumsuz bakış desteği olmadan:

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

Temel olarak fikir, önceki karakterleri kapmak ve negatif ileriye bakma ile eşleşmeyi hariç tutmak, ancak önceki n karakterin olmadığı durumlarla eşleşmektir. (burada n, geriye bakma uzunluğudur).

Yani söz konusu normal ifade:

(?<!([abcdefg]))m

şu dile çevirir:

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

Sizi ilgilendiren dizenin tam yerini bulmak için belirli grupları başka bir şeyle değiştirmek istiyorsanız, yakalama gruplarıyla oynamanız gerekebilir.


2
Bu doğru cevap olmalı. Bakınız: "So it would match the 'm' in 'jim' or 'm', but not 'jam'".replace(/(j(?!([abcdefg])).|^)m/g, "$1[MATCH]") döner "So it would match the 'm' in 'ji[MATCH]' or 'm', but not 'jam'" Oldukça basit ve işe yarıyor!
Asrail

41

Mijoja'nın stratejisi sizin durumunuza göre çalışır, ancak genel olarak değil:

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

Burada hedefin bir çift-l ile eşleştiği, ancak önünde "ba" varsa değil bir örnek. "Balll" kelimesine dikkat edin - gerçek gözetleme ilk 2 l'leri bastırmış olmalı, ancak 2. çiftle eşleşmelidir. Ancak ilk 2 l'leri eşleştirip ardından bu maçı yanlış pozitif olarak görmezden gelerek, normal ifade motoru bu eşleşmenin sonundan ilerler ve yanlış pozitif içindeki karakterleri yok sayar.


5
Ah, haklısın. Ancak, bu daha önce olduğundan çok daha yakın. Daha iyi bir şey ortaya çıkıncaya kadar bunu kabul edebilirim (javascript aslında görünümleri uygular gibi).
Andrew Ensley

33

kullanım

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

10
Bu hiçbir şey yapmaz: newStringher zaman eşit olacaktır string. Neden bu kadar çok oy veriyor?
MikeM

@MikeM: çünkü mesele sadece eşleşen bir tekniği göstermek.
hata

57
@bug. Hiçbir şey yapmayan bir gösteri garip bir gösteri. Bu sorunun cevabı, nasıl çalıştığına dair herhangi bir anlayışa sahip olmadan kopyalanmış ve yapıştırılmış gibi karşımıza çıkıyor. Böylece eşlik eden açıklama eksikliği ve herhangi bir şeyin eşleştiğini gösterememe.
MikeM

2
@MikeM: SO kuralı, soruyu yazılı olarak cevaplarsa, doğrudur. OP bir kullanım durumu belirtmedi
hata

7
Kavram doğru, ama evet çok iyi demo değil. ... JS konsolunda bu çalıştırmayı deneyin "Jim Jam Momm m".replace(/([abcdefg])?m/g, function($0, $1){ return $1 ? $0 : '[match]'; });. Geri dönmeli Ji[match] Jam Mo[match][match] [match]. Ancak Jason'ın aşağıda belirtildiği gibi, bazı uç durumlarda başarısız olabileceğini de unutmayın.
Simon East

11

Karakter kümenizi reddederek yakalamayan bir grup tanımlayabilirsiniz:

(?:[^a-g])m

... bu harflerden önce gelen her m NOT ile eşleşir .


2
Bence maç aslında önceki karakteri de kapsıyor.
Sam

4
^ bu doğrudur. Bir karakter sınıfı ... bir karakteri temsil eder! Yakalamayan grubunuzun yaptığı tek şey bu değeri bir değiştirme bağlamında kullanılabilir hale getirmemektedir.
İfadeniz

5
Cevabın orijinal problemi de (dize başlangıcı) çözmesi için, bir seçenek de içermesi gerekir, böylece sonuçta elde edilen normal ifade olacaktır (?:[^a-g]|^)m. Çalışan örnek için bkz. Regex101.com/r/jL1iW6/2 .
Johny Skovdal

Void mantığının kullanılması her zaman istenen etkiye sahip değildir.
GoldBishop

2

Ben str.split(/(?<!^)@/)Node.js 8 (bu lookbehind desteklemiyor) için nasıl elde :

str.split('').reverse().join('').split(/@(?!$)/).map(s => s.split('').reverse().join('')).reverse()

İşler? Evet (unicode test edilmemiştir). Hoş olmayan? Evet.


1

Mijoja fikrini takip ederek ve JasonS'ın maruz kaldığı sorunlardan yola çıkarak bu fikrim vardı; Ben biraz kontrol ama kendim emin değilim, bu yüzden js regex benden daha uzman biri tarafından bir doğrulama harika olurdu :)

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)

kişisel çıktım:

Fa[match] ball bi[match] bal[match] [match]ama

ilke, checkerherhangi bir iki karakterin arasındaki dizedeki her bir noktayı çağırmaktır , bu konum şu başlangıç ​​noktası olduğunda:

--- (burada 'ba', bu nedenle ..) istenmeyen boyutta herhangi bir alt dize (bu boyut biliniyorsa; aksi takdirde belki de yapmak daha zor olmalıdır)

--- --- veya dizenin başlangıcıysa bundan daha küçük: ^.?

ve bunu takiben,

--- gerçekte ne aranmalı (burada 'll').

Her çağrısında, checkerönceki değerin llistemediğimiz değer olup olmadığını kontrol etmek için bir test olacaktır ( !== 'ba'); bu durumda, başka bir işlev çağırırız ve doerstr'deki değişiklikleri yapacak olan bu ( ) olması gerekir, amaç bu ise veya daha genel olarak, manuel olarak işlemek için gerekli verileri girer tarama sonuçları str.

burada dizeyi değiştiriyoruz, bu nedenle replace, hepsi hesaplanan ve strhiçbir zaman değişmeyen, verilen konumları dengelemek için uzunluk farkının izini tutmamız gerekiyordu .

ilkel dizeler değişmez olduğundan, strtüm işlemin sonucunu saklamak için değişkeni kullanmış olabiliriz , ancak değiştirmelerle zaten karmaşık olan örneğin başka bir değişkenle ( str_done) daha açık olacağını düşündüm .

performans açısından oldukça sert olması gerektiğini tahmin ediyorum: '' içine '' tüm bu anlamsız yedekleri, this str.length-1zamanlar, artı burada doer tarafından manuel değiştirme, bu da bir çok dilimleme anlamına gelir ... muhtemelen bu özel yukarıdaki durumda olabilir biz eklemek istediğiniz yere etrafında parçalar halinde sadece bir kez dize keserek, gruplanabilir [match]ve .join()onu ing [match]kendisi.

diğer şey, daha karmaşık vakaları nasıl ele alacağını bilmiyorum, yani, sahte gözbebeği için karmaşık değerler ... uzunluk belki de elde etmek için en sorunlu veri olmak.

ve, checkerarkasında $ için istenmeyen değerlerin birden fazla olasılığı olması durumunda checker, aynı regex nesnesinin oluşturulmasını önlemek için başka bir normal ifade (dışarıda önbelleğe alınacak (oluşturulacak) en iyisi ile bir test yapmamız gerekecek her çağrıda checker) kaçınmaya çalıştığımız şey olup olmadığını bilmek.

umarım açık oldum; tereddüt etmeyin, ben daha iyi çalışacağım. :)


1

Durumunuzu kullanarak , örneğin büyük harfe dönüştürmek gibi bir şeyle değiştirmek istiyorsanız, yakalama grubundaki seti reddedebilirsiniz.mM

eşleştir ([^a-g])m, yerine koy$1M

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

([^a-g])aralıktaki ( ^) karakterlerle eşleşmez a-gve ilk yakalama grubunda saklar, böylece erişebilirsiniz $1.

Bulduğumuz Yani imiçinde jimve ile değiştirin iMhangi sonuçların jiM.


1

Daha önce de belirtildiği gibi, JavaScript şimdi gözlere izin veriyor. Daha eski tarayıcılarda hala bir geçici çözüme ihtiyacınız vardır.

Bahse girerim, sonucu tam olarak veren bir bakış açısı olmadan bir regex bulmanın bir yolu yoktur. Tek yapabileceğiniz gruplarla çalışmak. Bir regex'iniz olduğunu varsayalım (?<!Before)Wanted, Wantedmaç yapmak istediğiniz regex nerede Beforeve maçtan önce ne olmaması gerektiğini sayan regex. Yapabileceğiniz en iyi şey normal ifadeyi ortadan kaldırmak ve normal ifadeyi Beforekullanmaktır NotBefore(Wanted). İstenen sonuç ilk gruptur $1.

Sizin durumunuzda Before=[abcdefg]bunu reddetmek kolaydır NotBefore=[^abcdefg]. Yani normal ifade olurdu [^abcdefg](m). Eğer konumunu gerekirse Wanted, grup gerekir NotBeforede, bu yüzden istenilen sonuç ikinci grup olduğunu.

BeforeKalıp eşleşmeleri sabit bir uzunluğa nsahipse, yani kalıp tekrarlayan belirteç içermiyorsa, Beforekalıbı reddetmekten kaçınabilirsiniz ve normal ifadeyi kullanabilirsiniz (?!Before).{n}(Wanted), ancak yine de ilk grubu kullanmalı veya normal ifadeyi (?!Before)(.{n})(Wanted)kullanmalı ve ikincisini kullanmalısınız. grubudur. Bu örnekte, desen Beforeaslında sabit bir uzunluğa, yani 1'e sahiptir, bu nedenle normal ifadeyi (?![abcdefg]).(m)veya (?![abcdefg])(.)(m). Tüm eşleşmelerle ilgileniyorsanız, gbayrağı ekleyin, kod snippet'ime bakın:

function TestSORegEx() {
  var s = "Donald Trump doesn't like jam, but Homer Simpson does.";
  var reg = /(?![abcdefg])(.{1})(m)/gm;
  var out = "Matches and groups of the regex " + 
            "/(?![abcdefg])(.{1})(m)/gm in \ns = \"" + s + "\"";
  var match = reg.exec(s);
  while(match) {
    var start = match.index + match[1].length;
    out += "\nWhole match: " + match[0] + ", starts at: " + match.index
        +  ". Desired match: " + match[2] + ", starts at: " + start + ".";   
    match = reg.exec(s);
  }
  out += "\nResulting string after statement s.replace(reg, \"$1*$2*\")\n"
         + s.replace(reg, "$1*$2*");
  alert(out);
}

0

Bu etkili bir şekilde yapar

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

Örneği ara ve değiştir

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

Bunun çalışması için negatif arkaplan dizesinin 1 karakter uzunluğunda olması gerektiğini unutmayın.


1
Pek değil. "Jim" de "i" yi istemiyorum; sadece onlar". Ve aynı zamanda "m".match(/[^a-g]m/)sarılar null. Ben de bu durumda "m" istiyorum.
Andrew Ensley

-1

/(?![abcdefg])[^abcdefg]m/gi evet bu bir numara.


5
Kontrol (?![abcdefg])tamamen gereksizdir, çünkü [^abcdefg]bu karakterin eşleşmesini önlemek için işi zaten yapar.
nhahtdh

2
Bu, önceki karakter içermeyen bir 'm' ile eşleşmez.
Andrew Ensley
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.