वैश्विक ध्वज के साथ एक RegExp गलत परिणाम क्यों देता है?


277

जब मैं वैश्विक ध्वज और केस असंवेदनशील ध्वज का उपयोग करता हूं तो इस नियमित अभिव्यक्ति में क्या समस्या है? क्वेरी एक उपयोगकर्ता द्वारा निर्मित इनपुट है। इसका परिणाम [सत्य, सत्य] होना चाहिए।

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
result.push(re.test('Foo Bar'));
// result will be [true, false]

var reg = /^a$/g;
for(i = 0; i++ < 10;)
   console.log(reg.test("a"));


54
जावास्क्रिप्ट में RegExp के कई जाल में से एक में आपका स्वागत है। यह अजीबोगरीब साइड-इफेक्ट्स और अस्पष्ट कैविएट से भरे हुए, मैंने कभी भी मिले रिजेक्स प्रोसेसिंग के लिए सबसे खराब इंटरफेस में से एक है। आम तौर पर आप regex के साथ जो सामान्य कार्य करना चाहते हैं, उनमें से अधिकांश सही वर्तनी के लिए कठिन हैं।
बॉबिन

XRegExp एक अच्छे विकल्प की तरह दिखता है। xregexp.com
बारे

यहाँ भी उत्तर देखें: stackoverflow.com/questions/604860/…
Prestaul

एक समाधान, अगर आप इसे दूर कर सकते हैं, तो रेगेक्स शाब्दिक का उपयोग इसे बचाने के बजाय सीधे करना है re
थुदान

जवाबों:


350

RegExpवस्तु का ट्रैक रखता है lastIndexजहां एक मैच हुआ, तो बाद के मैचों पर उसे अंतिम बार उपयोग सूचकांक से शुरू होगा, 0. के बजाय एक नज़र डालें:

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));

alert(re.lastIndex);

result.push(re.test('Foo Bar'));

यदि आप lastIndexप्रत्येक परीक्षण के बाद मैन्युअल रूप से 0 पर रीसेट नहीं करना चाहते हैं , तो बस gध्वज को हटा दें ।

यहाँ एल्गोरिथ्म है कि चश्मा तय (धारा 15.10.6.2):

RegExp.prototype.exec (स्ट्रिंग)

नियमित अभिव्यक्ति के खिलाफ स्ट्रिंग का एक नियमित अभिव्यक्ति मैच करता है और एक सरणी वस्तु देता है जिसमें मैच के परिणाम होते हैं, या यदि स्ट्रिंग मेल नहीं खाती है तो स्ट्रिंग स्ट्रिंग (स्ट्रिंग) नियमित अभिव्यक्ति पैटर्न की घटना के लिए खोज की जाती है:

  1. आज्ञा देना मान के ToString (स्ट्रिंग)।
  2. लंबाई एस की लंबाई होने दें।
  3. बता दें कि lastIndex पिछलेIndex संपत्ति का मूल्य है।
  4. आइए मैं Toteteger (lastIndex) का मूल्य हो।
  5. यदि वैश्विक संपत्ति झूठी है, तो मुझे = 0 दें।
  6. अगर मुझे <0 या I> लंबाई है, तो lastIndex को 0 पर सेट करें और अशक्त लौटें।
  7. कॉल [[मैच]], यह तर्क एस और मैं दे रही है। यदि [[मैच]] विफलता लौटी, तो चरण 8 पर जाएं; अन्यथा r को उसका राज्य परिणाम मानें और चरण 10 पर जाएं।
  8. आइए = मैं + 1।
  9. चरण 6 पर जाएं।
  10. ई को r का एंडइंडेक्स मान दें।
  11. यदि वैश्विक संपत्ति सत्य है, तो lastIndex को e पर सेट करें।
  12. आज्ञा देना n लंबाई की r कैप्चर सरणी। (यह 15.10.2.1 के NCapturingParens के समान मूल्य है।)
  13. निम्नलिखित गुणों के साथ एक नया सरणी लौटाएँ:
    • सूचकांक संपत्ति पूर्ण स्ट्रिंग एस के भीतर मिलान किए गए प्रतिस्थापन की स्थिति पर सेट है।
    • इनपुट संपत्ति एस पर सेट है।
    • लंबाई संपत्ति n + 1 पर सेट है।
    • 0 गुण मिलान किए गए विकल्प (यानी ऑफसेट और समावेशी के बीच S का भाग) के बीच सेट है।
    • प्रत्येक पूर्णांक के लिए मैंने ऐसा किया कि I> 0 और I set n, ToString (i) नाम की संपत्ति को r के कैप्चर एरे के ith तत्व पर सेट करें।

83
यह यहां गैलेक्सी एपीआई डिजाइन के लिए सहयात्री की गाइड की तरह है। "वह नुकसान जो आप में गिर गया है वह कई वर्षों से कल्पना में पूरी तरह से प्रलेखित है, अगर आपने केवल जाँच करने के लिए परेशान किया था"
रैटसम

5
फ़ायरफ़ॉक्स का चिपचिपा झंडा वह नहीं करता है जो आप बिल्कुल करते हैं। बल्कि, यह इस तरह से कार्य करता है जैसे कि नियमित अभिव्यक्ति की शुरुआत में ^ थे, EXCEPT कि यह ^ स्ट्रिंग की शुरुआत के बजाय वर्तमान स्ट्रिंग स्थिति (lastIndex) से मेल खाती है । आप प्रभावी ढंग से परीक्षण कर रहे हैं अगर रेगेक्स "यहीं" के बजाय "लास्टइंडेक्स के बाद कहीं भी" मेल खाता है। आपके द्वारा प्रदत्त लिंक को देखें!
चल रहा

1
इस उत्तर का प्रारंभिक विवरण सही नहीं है। आपने कल्पना के चरण 3 को उजागर किया है जो कुछ भी नहीं कहता है। का वास्तविक प्रभाव lastIndexचरण 5, 6 और 11 में है। आपका प्रारंभिक कथन केवल तभी सही है जब अंतर्राष्ट्रीय फ्लैग सेट है।
प्रेस्टुल

@Prestaul हाँ, आप सही हैं कि यह वैश्विक ध्वज का उल्लेख नहीं करता है। यह शायद (याद नहीं कर सकता था कि मैं वापस क्या सोचा था) निहित है जिस तरह से सवाल फंसाया जाता है। उत्तर को संपादित करने या उसे हटाने और अपने उत्तर से लिंक करने के लिए स्वतंत्र महसूस करें। इसके अलावा, मैं आपको आश्वस्त करता हूं कि आप मुझसे बेहतर हैं। का आनंद लें!
आयनू जी। स्टेन स्टेन

@ Ionu previousG.Stan, क्षमा करें यदि मेरी पिछली टिप्पणी अटपटी लग रही थी, तो यह मेरा इरादा नहीं था। मैं इसे इस बिंदु पर संपादित नहीं कर सकता, लेकिन मैं अपनी टिप्पणी के आवश्यक बिंदु पर ध्यान आकर्षित करने के लिए चिल्लाने की कोशिश नहीं कर रहा था। मेरी गलती!
प्रेस्टेयूल

72

आप किसी एकल RegExpऑब्जेक्ट का उपयोग कर रहे हैं और इसे कई बार निष्पादित कर रहे हैं । प्रत्येक क्रमिक निष्पादन पर यह अंतिम मैच इंडेक्स से जारी रहता है।

आपको प्रत्येक निष्पादन से पहले शुरू से शुरू करने के लिए regex को "रीसेट" करना होगा:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]

यह कहते हुए कि हर बार एक नया RegExp ऑब्जेक्ट बनाने के लिए यह अधिक पठनीय हो सकता है (ओवरहेड न्यूनतम है क्योंकि RegExp को वैसे भी कैश किया जाता है):

result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));

1
या बस gध्वज का उपयोग न करें ।
melpomene

36

RegExp.prototype.testनियमित अभिव्यक्ति की lastIndexसंपत्ति को अपडेट करता है ताकि प्रत्येक परीक्षण शुरू हो जाए जहां अंतिम एक बंद हो गया। मैं String.prototype.matchइसका उपयोग करने का सुझाव देता हूं क्योंकि यह lastIndexसंपत्ति को अपडेट नहीं करता है:

!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true

नोट: !!इसे एक बूलियन में कनवर्ट करता है और फिर बूलियन को निष्क्रिय करता है ताकि यह परिणाम को दर्शाता है।

वैकल्पिक रूप से, आप बस lastIndexसंपत्ति रीसेट कर सकते हैं:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));

11

वैश्विक gझंडे को हटाने से आपकी समस्या ठीक हो जाएगी।

var re = new RegExp(query, 'gi');

होना चाहिए

var re = new RegExp(query, 'i');

0

आपको re.lastIndex = 0 सेट करने की आवश्यकता है क्योंकि g फ्लैग regex अंतिम मैच का ट्रैक रखता है, इसलिए परीक्षण उसी स्ट्रिंग का परीक्षण करने के लिए नहीं जाएगा, इसके लिए आपको re.lastIndex = 0 करना होगा।

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
re.lastIndex=0;
result.push(re.test('Foo Bar'));

console.log(result)


-1

/ G ध्वज का उपयोग करना हिट के बाद खोज जारी रखना बताता है।

यदि मैच सफल होता है, तो निष्पादन () विधि एक सरणी देता है और नियमित अभिव्यक्ति ऑब्जेक्ट के गुण अपडेट करता है।

अपनी पहली खोज से पहले:

myRegex.lastIndex
//is 0

पहली खोज के बाद

myRegex.lastIndex
//is 8

जी निकालें और यह निष्पादन के लिए प्रत्येक कॉल के बाद खोज से बाहर निकलता है ()।


ओपी उपयोग नहीं कर रहा है exec
melpomene

-1

मेरे पास समारोह था:

function parseDevName(name) {
  var re = /^([^-]+)-([^-]+)-([^-]+)$/g;
  var match = re.exec(name);
  return match.slice(1,4);
}

var rv = parseDevName("BR-H-01");
rv = parseDevName("BR-H-01");

पहला कॉल काम करता है। दूसरा कॉल नहीं करता है। sliceऑपरेशन एक शून्य मान के बारे में शिकायत। मुझे लगता है कि इस वजह से है re.lastIndex। यह अजीब है क्योंकि मैं अपेक्षा करता हूं कि RegExpजब भी फ़ंक्शन को कॉल किया जाता है और मेरे फ़ंक्शन के कई इनवोकेशनों में साझा नहीं किया जाता है तो एक नया आवंटित किया जाएगा।

जब मैंने इसे बदल दिया:

var re = new RegExp('^([^-]+)-([^-]+)-([^-]+)$', 'g');

तब मुझे lastIndexहोल्डओवर प्रभाव नहीं मिलता है। यह वैसा ही काम करता है जैसा मैं उससे उम्मीद करता हूं।

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