जावास्क्रिप्ट: नकारात्मक लग बराबर?


141

क्या जावास्क्रिप्ट नियमित अभिव्यक्ति में एक नकारात्मक लुकहैंड के बराबर प्राप्त करने का एक तरीका है ? मुझे एक स्ट्रिंग से मिलान करने की आवश्यकता है जो वर्णों के एक विशिष्ट सेट के साथ शुरू नहीं होती है।

ऐसा लगता है कि मैं एक रेगेक्स को खोजने में असमर्थ हूं, जो बिना असफल हो जाता है यदि स्ट्रिंग की शुरुआत में मिलान किया गया भाग पाया जाता है। नकारात्मक लगने वाले केवल एक ही उत्तर प्रतीत होते हैं, लेकिन जावास्क्रिप्ट में एक नहीं है।

संपादित करें: यह regex है जिसे मैं काम करना चाहूंगा, लेकिन यह नहीं है:

(?<!([abcdefg]))m

तो यह 'मी' को 'जीम' या 'एम' में मिलाएगा, लेकिन 'जैम' को नहीं।


रेगेक्स को पोस्ट करने पर विचार करें क्योंकि यह एक नकारात्मक लुकअप के साथ दिखेगा; इससे प्रतिक्रिया देना आसान हो सकता है।
डैनियल LeCheminant

1
जो लोग लुकबाइंड आदि को अपनाना चाहते हैं, वे कृपया ECMAScript 2016+ संगतता तालिका देखें
Wiktor Stribi'ew

@ WiktorStribiżew: 2018 की युक्ति में लुक-बिहाइंड जोड़े गए। क्रोम उनका समर्थन करता है, लेकिन फ़ायरफ़ॉक्स ने अभी भी कल्पना को लागू नहीं किया है
लोनी बेस्ट

क्या इसके पीछे भी एक नजर की जरूरत है? किस बारे में (?:[^abcdefg]|^)(m)? में के रूप में"mango".match(/(?:[^abcdefg]|^)(m)/)[1]
slebetman

जवाबों:


57

Lookbehind दावे कर ली स्वीकार किए जाते हैं में ECMAScript विनिर्देश 2018 में।

सकारात्मक लग रहे उपयोग:

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

नकारात्मक खोज के उपयोग:

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

प्लेटफ़ॉर्म समर्थन:


2
क्या कोई पॉलीफ़िल है?
किल्ली

1
@ किली जहाँ तक मुझे पता नहीं है, और मुझे संदेह है कि वहाँ कभी भी होगा, जैसा कि एक बनाना संभवतः बहुत ही अव्यावहारिक होगा (IE जेएस में पूर्ण रेगेक्स कार्यान्वयन लिख रहा है)
ओक्कू

बाबेल प्लगइन का उपयोग करने के बारे में क्या, क्या ES5 के लिए संकलित किया जाना संभव है या पहले से ही समर्थित ES6?
स्टीफन जे

1
@IlpoOksanen मुझे लगता है कि आपका मतलब है कि आप RegEx कार्यान्वयन का विस्तार कर रहे हैं .. जो कि पॉलीफ़िल करते हैं .... और जावास्क्रिप्ट में तर्क लिखने के साथ कुछ भी गलत नहीं है
neaumusic

1
तुम्हारी किस बारे में बोलने की इच्छा थी? लगभग सभी प्रस्ताव अन्य भाषाओं से प्रेरित हैं और वे हमेशा अन्य भाषाओं के वाक्यविन्यास और शब्दार्थ से मेल खाना पसंद करेंगे जहां यह मुहावरेदार जेएस और पीछे की संगतता के संदर्भ में समझ में आता है। मुझे लगता है कि मैंने स्पष्ट रूप से कहा है कि सकारात्मक और नकारात्मक दोनों प्रकार के लुकहाइंड को 2017 में 2018 की कल्पना में स्वीकार किया गया था और मैंने सूत्रों को लिंक दिया था। इसके अलावा, मैंने विस्तार से बताया कि कौन से प्लेटफ़ॉर्म ने कहा कि विनिर्देश को लागू करें और अन्य प्लेटफार्मों की स्थिति क्या है - और तब से इसे अपडेट कर रहे हैं। स्वाभाविक रूप से यह अंतिम Regexp सुविधा नहीं है जिसे हम देखेंगे
Okku

83

2018 से, लुकहाइबर्ड दावे ईसीएमएस्क्रिप्ट भाषा विनिर्देश का हिस्सा हैं ।

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

उत्तर पूर्व २०१8

जैसा कि जावास्क्रिप्ट नकारात्मक रूपांतर का समर्थन करता है , ऐसा करने का एक तरीका है:

  1. इनपुट स्ट्रिंग को उल्टा करें

  2. उलटे रेगेक्स के साथ मैच

  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:

@ 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: Ø

36
इस दृष्टिकोण के साथ समस्या यह है कि यह तब काम नहीं करता है जब आपके पास लुकहैड और लुकबाइंड दोनों हों
kboom

3
क्या आप कृपया एक कार्यकारी उदाहरण दिखा सकते हैं, कहते हैं कि मैं मैच करना चाहता हूं, max-heightलेकिन line-heightमैं केवल मैच ही नहीं चाहता height
हूं

यदि कार्य दो लगातार समान प्रतीकों (और 2 से अधिक नहीं) को बदलने में मदद करता है जो कुछ प्रतीक से पहले नहीं हैं। ''(?!\()में अक्षर लोप का स्थान ले लेगा ''(''test'''''''testदूसरे छोर से, इस प्रकार छोड़ने (''test'NNNtestके बजाय (''testNNN'test
विकटोरिया स्ट्रीब्यूव

61

मान लीजिए कि आप intपहले से नहीं सभी को ढूंढना चाहते हैं unsigned:

नकारात्मक रूप के लिए समर्थन के साथ पीछे:

(?<!unsigned )int

नकारात्मक दिखावे के समर्थन के बिना:

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

मूल रूप से विचार n पूर्ववर्ती वर्णों को पकड़ना है और नकारात्मक रूप से आगे के साथ मैच को बाहर करना है, लेकिन उन मामलों से भी मेल खाना है जहां कोई पूर्ववर्ती n वर्ण नहीं हैं। (जहां n लुक-बैक की लंबाई है)।

तो सवाल में regex:

(?<!([abcdefg]))m

इसका अनुवाद करेंगे:

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

आपको स्ट्रिंग के सटीक स्थान का पता लगाने के लिए समूहों को कैप्चर करने के साथ खेलने की आवश्यकता हो सकती है जो आपकी रुचि है या आप विशिष्ट भाग को किसी अन्य चीज़ से बदलना चाहते हैं।


2
यह सही उत्तर होना चाहिए। देखें: "So it would match the 'm' in 'jim' or 'm', but not 'jam'".replace(/(j(?!([abcdefg])).|^)m/g, "$1[MATCH]") रिटर्न "So it would match the 'm' in 'ji[MATCH]' or 'm', but not 'jam'" यह बहुत आसान है और यह काम करता है!
असिल

41

मिजोया की रणनीति आपके विशिष्ट मामले के लिए काम करती है लेकिन सामान्य रूप से नहीं:

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

यहां एक उदाहरण है जहां लक्ष्य एक डबल-एल से मेल खाना है, लेकिन नहीं अगर यह "बा" से पहले हो। "बॉलल" शब्द पर ध्यान दें - सच्चा लुकहैंड को पहले 2 एल को दबा देना चाहिए था लेकिन 2 के जोड़े से मेल खाना चाहिए। लेकिन पहले 2 l का मिलान करके और फिर उस मैच को एक झूठे सकारात्मक के रूप में अनदेखा करते हुए, regexp इंजन उस मैच के अंत से आगे बढ़ता है , और झूठे सकारात्मक के भीतर किसी भी वर्ण की उपेक्षा करता है।


5
आह, आप सही हैं। हालांकि, यह पहले की तुलना में बहुत करीब है। मैं इसे तब तक स्वीकार कर सकता हूं जब तक कि कुछ बेहतर न हो (जैसे जावास्क्रिप्ट वास्तव में लुकहाइंड को लागू करना)।
एंड्रयू एन्सले

33

उपयोग

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

10
यह कुछ भी नहीं करता है: newStringहमेशा बराबर होगा string। इतने अपवित्र क्यों?
मिकएम

@ माइक: क्योंकि बिंदु केवल एक मिलान तकनीक प्रदर्शित करने के लिए है।
बग

57
@bug। एक प्रदर्शन जो कुछ नहीं करता है वह एक अजीब तरह का प्रदर्शन है। इसका उत्तर यह है कि यह कैसे काम करता है, इसकी किसी भी समझ के बिना सिर्फ कॉपी और पेस्ट किया जाता है। इस प्रकार, स्पष्टीकरण के साथ कमी और यह प्रदर्शित करने में विफलता कि कुछ भी मिलान किया गया है।
माइक एम

2
@ माइक: एसओ का नियम है, अगर वह प्रश्न का लिखित रूप में उत्तर देता है, तो यह सही है। ओपी ने एक उपयोग के मामले को निर्दिष्ट नहीं किया
बग

7
अवधारणा सही है, लेकिन हाँ यह बहुत अच्छी तरह से डेमो नहीं होगा। जे एस कंसोल में इस चलाने की कोशिश करें ... "Jim Jam Momm m".replace(/([abcdefg])?m/g, function($0, $1){ return $1 ? $0 : '[match]'; });। इसे वापस लौटना चाहिए Ji[match] Jam Mo[match][match] [match]। लेकिन यह भी ध्यान दें कि जैसा कि जेसन ने नीचे उल्लेख किया है, यह कुछ धार मामलों पर विफल हो सकता है।
साइमन ईस्ट

11

आप अपने चरित्र सेट की उपेक्षा करके एक गैर-कैप्चरिंग समूह को परिभाषित कर सकते हैं:

(?:[^a-g])m

... जो उन पत्रों में से किसी से भी पहले से मेल m नहीं खाता ।


2
मुझे लगता है कि मैच वास्तव में पूर्ववर्ती चरित्र को भी कवर करेगा।
सैम

4
^ यह सच है। एक चरित्र वर्ग का प्रतिनिधित्व करता है ... एक चरित्र! आपके सभी गैर-कैप्चरिंग समूह जो कर रहे हैं, वह मान किसी संदर्भ में उपलब्ध नहीं करा रहा है। आपकी अभिव्यक्ति यह नहीं कह रही है "हर मी उन अक्षरों में से किसी से पहले नहीं है" यह कह रहा है "हर एम एक चरित्र से पहले है जो उन पत्रों में से कोई भी नहीं है"
theflowersoftime

5
मूल समस्या (स्ट्रिंग की शुरुआत) को हल करने के उत्तर के लिए, इसमें एक विकल्प भी शामिल होना चाहिए, इसलिए परिणामी rexx होगा (?:[^a-g]|^)m। रनिंग उदाहरण के लिए regex101.com/r/jL1iW6/2 देखें ।
जॉनी स्कोवडाल

शून्य तर्क का उपयोग करने से हमेशा वांछित प्रभाव नहीं होता है।
गोल्डबिशॉप

2

यह मैंने str.split(/(?<!^)@/)Node.js 8 के लिए हासिल किया (जो लुकअप का समर्थन नहीं करता):

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

काम करता है? हां (यूनिकोड अनटाइटेड)। अप्रिय? हाँ।


1

मिजो के विचार के बाद, और जेसन द्वारा उजागर समस्याओं से ड्राइंग, मुझे यह विचार था; मैंने थोड़ी जाँच की लेकिन मुझे खुद पर यकीन नहीं है, इसलिए 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जब भी यह स्थिति आरंभिक बिंदु हो, किसी भी दो वर्णों के बीच स्ट्रिंग में प्रत्येक बिंदु पर :

--- जो नहीं चाहता है के आकार का कोई भी विकल्प (यहां 'ba', इस प्रकार).. ) (यदि वह आकार ज्ञात है, अन्यथा उसे करना कठिन होना चाहिए)

--- या उससे छोटा है अगर यह स्ट्रिंग की शुरुआत है: ^.?

और, इसके बाद,

--- वास्तव में क्या मांगा जाना है (यहां 'll' )।

प्रत्येक कॉल पर checker, यह जांचने के लिए एक परीक्षण होगा llकि क्या मूल्य पहले वह नहीं है जो हम नहीं चाहते ( !== 'ba'); अगर ऐसा है, तो हम एक अन्य फ़ंक्शन को कॉल करते हैं, और इसे यह एक होना होगा ( doer) जो str पर बदलाव करेगा, यदि उद्देश्य यह एक है, या अधिक उदारता से, जो इनपुट में आवश्यक डेटा को मैन्युअल रूप से प्रोसेस करने के लिए मिलेगा। के स्कैनिंग के परिणाम str

यहां हम स्ट्रिंग को बदलते हैं, इसलिए हमें दिए गए स्थानों को ऑफसेट करने के लिए लंबाई के अंतर का पता लगाने की आवश्यकता है , जो कि replaceसभी गणना करता है str, जो खुद कभी नहीं बदलता है।

चूंकि आदिम तार अपरिवर्तनीय हैं, इसलिए हम strपूरे ऑपरेशन के परिणाम को संग्रहीत करने के लिए चर का उपयोग कर सकते थे , लेकिन मुझे लगा कि उदाहरण, पहले से ही प्रतिकृति द्वारा जटिल, दूसरे चर ( str_done) के साथ स्पष्ट होगा ।

मुझे लगता है कि प्रदर्शन के मामले में यह बहुत कठोर होना चाहिए: '' में '' के सभी निरर्थक प्रतिस्थापन, this str.length-1समय, प्लस यहाँ कर्ता द्वारा मैनुअल प्रतिस्थापन, जिसका अर्थ है बहुत अधिक टुकड़ा करना ... शायद इस विशिष्ट उपरोक्त मामले में हो सकता है समूहीकृत किया जा सकता है, स्ट्रिंग को केवल एक बार टुकड़ों में काटकर, जहां हम इसे सम्मिलित करना चाहते हैं [match]और .join()इसे [match]अपने साथ जोड़ सकते हैं।

दूसरी बात यह है कि मुझे नहीं पता कि यह और अधिक जटिल मामलों को कैसे निपटेगा, यानी फर्जी लुकहैब के लिए जटिल मूल्य ... लंबाई शायद सबसे अधिक समस्याग्रस्त डेटा प्राप्त करने के लिए।

और, checker$ पीछे के गैर-वांछनीय मूल्यों की कई संभावनाओं के मामले में, हमें इसके साथ एक परीक्षण करना होगा, फिर भी एक और रेगेक्स (कैश्ड बनाया जाना) checkerसबसे अच्छा है, उसी रेगेक्स ऑब्जेक्ट से बचने के लिए। प्रत्येक कॉल पर checker) यह जानने के लिए कि क्या हम बचने के लिए चाहते हैं या नहीं।

आशा है कि मैं स्पष्ट हो गया हूँ; अगर संकोच न करें, तो मैं बेहतर कोशिश करूँगा। :)


1

अपने मामले का उपयोग करते हुए, यदि आप 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


1

जैसा कि पहले उल्लेख किया गया है, जावास्क्रिप्ट अब लुकअपहाइंड की अनुमति देता है। पुराने ब्राउज़रों में आपको अभी भी वर्कअराउंड की आवश्यकता है।

मैं शर्त लगाता हूँ कि वहाँ कोई रास्ता नहीं है rebex खोज के बिना कोई खोजे कि वास्तव में परिणाम बचाता है। आप सभी कर सकते हैं समूहों के साथ काम कर रहा है। मान लीजिए कि आपके पास एक regex है (?<!Before)Wanted, Wantedवह regex कहां है जिसे आप मिलान करना चाहते हैं और Beforeवह regex है जो इस बात की गणना करता है कि मैच से पहले क्या नहीं होना चाहिए। सबसे अच्छा आप कर सकते हैं regex को नकारना और regex Beforeका उपयोग करना NotBefore(Wanted)। वांछित परिणाम पहला समूह है $1

आपके मामले में Before=[abcdefg]जिसे नकारना आसान है NotBefore=[^abcdefg]। तो रेगेक्स होगा [^abcdefg](m)। यदि आप की स्थिति की आवश्यकता है Wanted, तो आपको समूह NotBeforeभी चाहिए , ताकि वांछित परिणाम दूसरा समूह हो।

यदि Beforeपैटर्न के मैचों की एक निश्चित लंबाई होती है n, अर्थात यदि पैटर्न में दोहराव वाले टोकन नहीं हैं, तो आप Beforeपैटर्न को नकारने से बच सकते हैं और नियमित अभिव्यक्ति का उपयोग कर सकते हैं (?!Before).{n}(Wanted), लेकिन फिर भी पहले समूह का उपयोग करना होगा या नियमित अभिव्यक्ति (?!Before)(.{n})(Wanted)का उपयोग करना होगा और दूसरे का उपयोग करना होगा। समूह। इस उदाहरण में, पैटर्न में Beforeवास्तव में एक निश्चित लंबाई है, जिसका नाम 1 है, इसलिए रेगेक्स (?![abcdefg]).(m)या का उपयोग करें (?![abcdefg])(.)(m)। यदि आप सभी मैचों में रुचि रखते हैं, तो gध्वज जोड़ें , मेरा कोड स्निपेट देखें:

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

यह प्रभावी रूप से करता है

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

उदाहरण को खोजें और बदलें

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

ध्यान दें कि काम करने के लिए नकारात्मक लुक-पीछे स्ट्रिंग 1 वर्ण लंबा होना चाहिए।


1
काफी नहीं। "जीम" में, मुझे "मैं" नहीं चाहिए; बस उन्हें"। और "m".match(/[^a-g]m/)yeilds nullरूप में अच्छी तरह। मैं उस मामले में भी "एम" चाहता हूं।
एंड्रयू एनस्ले

-1

/(?![abcdefg])[^abcdefg]m/gi हाँ यह एक चाल है।


5
चेक (?![abcdefg])पूरी तरह से बेमानी है, क्योंकि [^abcdefg]पहले से ही उन चरित्र को मिलान करने से रोकने के लिए अपना काम करता है।
नाहदह

2
यह पूर्ववर्ती वर्णों के साथ एक 'मी' से मेल नहीं खाएगा।
एंड्रयू एनस्ले
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.