जावास्क्रिप्ट regex में नाम कैप्चरिंग समूह?


208

जहां तक ​​मुझे पता है कि जावास्क्रिप्ट में कैप्चरिंग ग्रुप नाम की कोई चीज नहीं है। समान कार्यक्षमता प्राप्त करने का वैकल्पिक तरीका क्या है?


1
जावास्क्रिप्ट में कैप्चर समूह संख्या से हैं .. $ 1 पहला कब्जा किया गया समूह है, $ 2, $ 3 ... $ 99 तक है लेकिन ऐसा लगता है कि आप कुछ और चाहते हैं - जो मौजूद नहीं है
एरिक

24
@Erik आप के बारे में बात कर रहे हैं गिने कब्जा समूहों, ओपी के बारे में बात नामित कब्जा समूहों। वे मौजूद हैं, लेकिन हम जानना चाहते हैं कि क्या जेएस में उनके लिए समर्थन है।
अल्बा मेंडेज़

4
जावास्क्रिप्ट में नाम रेगेक्स को लाने का एक प्रस्ताव है , लेकिन अगर हम कभी ऐसा करते हैं, तो यह सालों पहले हो सकता है।
फ्रीगेट

फ़ायरफ़ॉक्स ने मुझे एक वेबसाइट पर नामांकित समूहों का उपयोग करने की कोशिश करने के लिए दंडित किया ... मेरी अपनी गलती है। stackoverflow.com/a/58221254/782034
निक ग्रेली

जवाबों:


134

ECMAScript 2018 का नाम जावास्क्रिप्ट कैप्चरिंग समूहों को जावास्क्रिप्ट में शामिल करना है।

उदाहरण:

  const auth = 'Bearer AUTHORIZATION_TOKEN'
  const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
  console.log(token) // "Prints AUTHORIZATION_TOKEN"

यदि आपको पुराने ब्राउज़रों का समर्थन करने की आवश्यकता है, तो आप सामान्य (क्रमांकित) कैप्चरिंग समूहों के साथ सब कुछ कर सकते हैं जो आप नामित कैप्चरिंग समूहों के साथ कर सकते हैं, आपको बस संख्याओं पर नज़र रखने की आवश्यकता है - जो आपके समूह में कैप्चर करने के क्रम के बोझिल हो सकते हैं रेगेक्स परिवर्तन।

नामित कैप्चरिंग समूहों के केवल दो "संरचनात्मक" फायदे हैं जिनके बारे में मैं सोच सकता हूं:

  1. कुछ रेगेक्स फ्लेवर (.NET और JGSoft, जहाँ तक मुझे पता है) में, आप अपने regex में अलग-अलग समूहों के लिए एक ही नाम का उपयोग कर सकते हैं ( उदाहरण के लिए यहां देखें जहां यह मामला है )। लेकिन अधिकांश रेगेक्स जायके इस कार्यक्षमता का समर्थन नहीं करते हैं।

  2. यदि आपको ऐसी स्थिति में गिने हुए कैप्चरिंग समूहों को संदर्भित करने की आवश्यकता होती है, जहां वे अंकों से घिरे होते हैं, तो आपको समस्या हो सकती है। मान लीजिए कि आपकी एक अंकों के लिए एक शून्य जोड़ना चाहते हैं और इसलिए बदलना चाहते हैं का कहना है कि (\d)साथ $10। जावास्क्रिप्ट में, यह काम करेगा (जब तक कि आपके regex में 10 से कम कैप्चरिंग ग्रुप है), लेकिन पर्ल को लगता है कि आप संख्या के 10बजाय बैकरेफेरेंस नंबर की तलाश कर रहे हैं 1, उसके बाद a 0। पर्ल में, आप ${1}0इस मामले में उपयोग कर सकते हैं ।

इसके अलावा, कैप्चरिंग समूहों के नाम "सिंटैक्टिक शुगर" हैं। यह केवल तब कैप्चरिंग समूहों का उपयोग करने में मदद करता है जब आपको वास्तव में उनकी आवश्यकता होती है और गैर-कैप्चरिंग समूहों का उपयोग करने के लिए(?:...) अन्य सभी परिस्थितियों में ।

जावास्क्रिप्ट के साथ बड़ी समस्या (मेरी राय में) यह है कि यह वर्बोज़ रीग्क्स का समर्थन नहीं करता है जो पठनीय, जटिल नियमित अभिव्यक्तियों के निर्माण को बहुत आसान बना देगा।

स्टीव लेविथान की एक्सएग्जिप लाइब्रेरी इन समस्याओं को हल करती है।


5
कई फ्लेवर एक ही कैप्चरिंग ग्रुप नेम का एक रेगेक्स में कई बार उपयोग करने की अनुमति देते हैं। लेकिन केवल .NET और पर्ल 5.10+ मैच में भाग लेने वाले नाम के अंतिम समूह द्वारा कैप्चर किए गए मान को रखते हुए इसे विशेष रूप से उपयोगी बनाते हैं।
स्लीवथान

103
बड़ा फायदा यह है: आप अपने RegExp को बदल सकते हैं, कोई नंबर-टू-वैरिएबल मैपिंग नहीं। गैर-कैप्चरिंग समूह एक मामले को छोड़कर, इस समस्या को हल करते हैं: क्या होगा यदि समूहों का क्रम बदल जाए? इसके अलावा, इस अतिरिक्त वर्ण को अन्य समूहों पर डालने की घोषणा की जा रही है ...
अल्बा मेंडेज़

55
तथाकथित सिंथैटिक शुगर कोड की पठनीयता को मीठा करने में मदद करता है!
मृकफ जूल

1
मुझे लगता है कि नामित समूहों पर कब्जा करने का एक और कारण है जो वास्तव में मूल्यवान है। उदाहरण के लिए, यदि आप स्ट्रिंग से किसी दिनांक को पार्स करने के लिए एक regex का उपयोग करना चाहते हैं, तो आप एक लचीला फ़ंक्शन लिख सकते हैं जो मान और regex लेता है। जब तक रेगेक्स ने वर्ष, महीने और तारीख के लिए कैप्चर का नाम दिया है, आप न्यूनतम कोड के साथ नियमित अभिव्यक्ति की एक सरणी के माध्यम से चला सकते हैं।
डेवी वोजेल

4
अक्टूबर 2019 तक, फ़ायरफ़ॉक्स, IE 11 और माइक्रोसॉफ्ट एज (प्री-क्रोमियम) समूह कैप्चर नाम का समर्थन नहीं करते हैं। अधिकांश अन्य ब्राउज़र (यहां तक ​​कि ओपेरा और सैमसंग मोबाइल) करते हैं। caniuse.com/…
JDB को अभी भी

63

आप एक्सरेपाइप का उपयोग कर सकते हैं , नियमित रूप से अभिव्यक्ति के एक संवर्धित, एक्स्टेंसिबल, क्रॉस-ब्राउज़र कार्यान्वयन, अतिरिक्त सिंटैक्स, झंडे और विधियों के लिए समर्थन सहित:

  • नए रेगेक्स और रिप्लेसमेंट टेक्स्ट सिंटैक्स जोड़ता है, जिसमें नामित कैप्चर के लिए व्यापक समर्थन शामिल है ।
  • दो नए regex झंडे जोड़ता है:, sडॉट को सभी वर्णों (उर्फ डॉटॉल या सिंगललाइन मोड) से जोड़ने के xलिए , और , मुक्त-रिक्ति और टिप्पणियों (उर्फ विस्तारित मोड) के लिए।
  • कार्यों और विधियों का एक सूट प्रदान करता है जो जटिल रेगेक्स प्रसंस्करण को एक हवा बनाते हैं।
  • रेगेक्स व्यवहार और वाक्य रचना में स्वचालित रूप से सबसे आम तौर पर सामना करना पड़ा क्रॉस-ब्राउज़र असंगतताओं को ठीक करता है।
  • आपको आसानी से प्लगइन्स बनाने और उपयोग करने देता है जो XRegExp की नियमित अभिव्यक्ति भाषा में नए वाक्यविन्यास और झंडे जोड़ते हैं।

60

एक अन्य संभावित समाधान: एक वस्तु बनाएं जिसमें समूह के नाम और अनुक्रमित हों।

var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };

फिर, समूहों को संदर्भित करने के लिए ऑब्जेक्ट कुंजियों का उपयोग करें:

var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];

यह रेगेक्स के परिणामों का उपयोग करके कोड की पठनीयता / गुणवत्ता में सुधार करता है, लेकिन स्वयं रेगेक्स की पठनीयता नहीं।


58

ES6 में आप अपने समूहों को पकड़ने के लिए सरणी विनाशकारी का उपयोग कर सकते हैं:

let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];

// count === '27'
// unit === 'months'

नोटिस:

  • अंतिम कॉमा में पहला कॉमा letपरिणामी सरणी का पहला मूल्य छोड़ता है, जो पूरे मिलान स्ट्रिंग है
  • || []के बाद .exec()जब यहां कोई मिलान नहीं (क्योंकि एक destructuring त्रुटि पाएगा .exec()वापस आ जाएगी null)

1
पहला अल्पविराम इसलिए है क्योंकि मैच द्वारा लौटाए गए सरणी का पहला तत्व इनपुट अभिव्यक्ति है, है ना?
एमिलियो ग्रिसोलिया

1
String.prototype.matchएक सरणी देता है: स्थिति 0 पर पूरे मिलान स्ट्रिंग, उसके बाद किसी भी समूह। पहला अल्पविराम कहता है "तत्व को स्थिति 0 पर छोड़ें"
fregante

2
ट्रांसप्लिंग या ES6 + लक्ष्य रखने वालों के लिए मेरा पसंदीदा उत्तर यहां है। यह जरूरी नहीं है कि असंगतता त्रुटियों के साथ-साथ नामित सूचकांकों को भी रोका जा सकता है यदि उदाहरण के लिए एक पुन: उपयोग किए गए rexx में परिवर्तन होता है, लेकिन मुझे लगता है कि यहां संक्षिप्तता आसानी से बनती है। मैंने उन स्थानों RegExp.prototype.execपर चयन किया है String.prototype.matchजहां स्ट्रिंग हो सकती है nullया undefined
माइक हिल

22

अद्यतन: यह अंत में इसे जावास्क्रिप्ट (ECMAScript 2018) में बनाया गया!


नामित कैप्चरिंग समूह इसे बहुत जल्द जावास्क्रिप्ट में बना सकते हैं।
इसके लिए प्रस्ताव पहले ही चरण 3 में है।

कैप्चर समूह को (?<name>...)किसी भी पहचानकर्ता के नाम के लिए सिंटैक्स का उपयोग करके कोणीय कोष्ठक के अंदर एक नाम दिया जा सकता है । एक तिथि के लिए नियमित अभिव्यक्ति तब लिखी जा सकती है /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u। प्रत्येक नाम अद्वितीय होना चाहिए और ECMAScript IdentifierName के लिए व्याकरण का पालन करना चाहिए

नामांकित समूहों को नियमित अभिव्यक्ति परिणाम के समूह गुण की संपत्तियों से एक्सेस किया जा सकता है। समूहों के क्रमांकित संदर्भ भी बनाए जाते हैं, जैसे कि गैर-नामित समूहों के लिए। उदाहरण के लिए:

let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';

यह इस समय एक चरण 4 का प्रस्ताव है।
गोटो 0

यदि आप '18 का उपयोग कर रहे हैं, तो विनाशकारी होने के साथ-साथ सभी जा सकते हैं; let {year, month, day} = ((result) => ((result) ? result.groups : {}))(re.exec('2015-01-02'));
हैशबोर्न

6

पकड़े गए समूहों का नामकरण एक चीज़ प्रदान करता है: जटिल नियमित अभिव्यक्तियों के साथ कम भ्रम।

यह वास्तव में आपके उपयोग के मामले पर निर्भर करता है, लेकिन शायद अपने regex मुद्रण सुंदर मदद कर सकता है।

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

टिप्पणियाँ तब दूसरों को भी दिखाने में मदद कर सकती हैं जो आपके कोड को पढ़ते हैं, आपने क्या किया है।

बाकी के लिए मुझे टिम्स के जवाब से सहमत होना चाहिए।


5

एक नोड.जेएस लाइब्रेरी है जिसका नाम है- रेगेक्सपी जिसे आप अपने नोड में उपयोग कर सकते हैं। जेएस परियोजनाएं (ब्राउज़र पर लाइब्रेरी या अन्य पैकेजिंग स्क्रिप्ट के साथ पैकेजिंग करके ब्राउज़र में)। हालाँकि, लाइब्रेरी का उपयोग नियमित अभिव्यक्तियों के साथ नहीं किया जा सकता है जिसमें गैर-नामित कैप्चरिंग समूह होते हैं।

यदि आप अपने नियमित अभिव्यक्ति में खुलने वाले कैप्चरिंग ब्रेसिज़ की गिनती करते हैं, तो आप अपने कैप्चरिंग समूह में नामित कैप्चरिंग समूहों और गिने हुए कैप्चरिंग समूहों के बीच एक मैपिंग बना सकते हैं और स्वतंत्र रूप से मिक्स एंड मैच कर सकते हैं। आपको बस regex का उपयोग करने से पहले समूह के नाम हटाने होंगे। मैंने तीन कार्य लिखे हैं जो प्रदर्शित करते हैं। इस gist को देखें: https://gist.github.com/gbirke/2cc2370135b665eee3ef


यह आश्चर्यजनक हल्का है, मैं इसे
आजमाऊंगा

क्या यह जटिल नियमित अभिव्यक्तियों में नियमित समूहों के अंदर नेस्टेड नामित समूहों के साथ काम करता है?
एलासको

यह सही नहीं है। बग जब: getMap ("(((a। B (: <foo> c)))"); फू तीसरा समूह होना चाहिए, दूसरा नहीं। /((a|b(c)))/g.exec("bc "); ["बीसी", "बीसी", "बीसी", "सी"]
एल्सजाको १४'१६ बजे

3

जैसा कि टिम पीटरज़क ने कहा कि ECMAScript 2018 जावास्क्रिप्ट कैग समूह में नामित कैप्चरिंग समूहों का परिचय देता है। लेकिन उपरोक्त उत्तरों में मुझे जो नहीं मिला वह यह था कि रेगेक्स में नामित कैद समूह का उपयोग कैसे किया जाए।

आप इस वाक्य रचना के साथ नामांकित समूह का उपयोग कर सकते हैं \k<name>:। उदाहरण के लिए

var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/

और जैसा कि फॉरिविन ने कहा है कि आप निम्न परिणाम में प्राप्त समूह का उपयोग कर सकते हैं:

let result = regexObj.exec('2019-28-06 year is 2019');
// result.groups.year === '2019';
// result.groups.month === '06';
// result.groups.day === '28';

  var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/mgi;

function check(){
    var inp = document.getElementById("tinput").value;
    let result = regexObj.exec(inp);
    document.getElementById("year").innerHTML = result.groups.year;
    document.getElementById("month").innerHTML = result.groups.month;
    document.getElementById("day").innerHTML = result.groups.day;
}
td, th{
  border: solid 2px #ccc;
}
<input id="tinput" type="text" value="2019-28-06 year is 2019"/>
<br/>
<br/>
<span>Pattern: "(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>";
<br/>
<br/>
<button onclick="check()">Check!</button>
<br/>
<br/>
<table>
  <thead>
    <tr>
      <th>
        <span>Year</span>
      </th>
      <th>
        <span>Month</span>
      </th>
      <th>
        <span>Day</span>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <span id="year"></span>
      </td>
      <td>
        <span id="month"></span>
      </td>
      <td>
        <span id="day"></span>
      </td>
    </tr>
  </tbody>
</table>


2

जब आप वैनिला जावास्क्रिप्ट के साथ ऐसा नहीं कर सकते हैं, तो शायद आप कुछ Array.prototypeफ़ंक्शन का उपयोग कर सकते हैं जैसे Array.prototype.reduceकुछ जादू का उपयोग करके अनुक्रमित मैचों को नामांकित लोगों में बदलना ।

जाहिर है, निम्नलिखित समाधान के लिए मैच की आवश्यकता होगी:

// @text Contains the text to match
// @regex A regular expression object (f.e. /.+/)
// @matchNames An array of literal strings where each item
//             is the name of each group
function namedRegexMatch(text, regex, matchNames) {
  var matches = regex.exec(text);

  return matches.reduce(function(result, match, index) {
    if (index > 0)
      // This substraction is required because we count 
      // match indexes from 1, because 0 is the entire matched string
      result[matchNames[index - 1]] = match;

    return result;
  }, {});
}

var myString = "Hello Alex, I am John";

var namedMatches = namedRegexMatch(
  myString,
  /Hello ([a-z]+), I am ([a-z]+)/i, 
  ["firstPersonName", "secondPersonName"]
);

alert(JSON.stringify(namedMatches));


यह बहुत मजेदार है। मैं सिर्फ सोच रहा हूँ .. क्या यह संभव नहीं होगा कि एक regex फंक्शन बनाया जाए जो एक कस्टम regex को स्वीकार करे? ताकि आप जैसे जा सकेंvar assocArray = Regex("hello alex, I am dennis", "hello ({hisName}.+), I am ({yourName}.+)");
Forivin

@Forivin स्पष्ट रूप से आप आगे जा सकते हैं और इस सुविधा को विकसित कर सकते हैं। इसे काम में
लाना

आप RegExpकिसी फ़ंक्शन को उसके प्रोटोटाइप में जोड़कर उसका विस्तार कर सकते हैं ।
श्री टीए

@ श्री एएफएआईके, इसे निर्मित वस्तुओं का विस्तार करने की अनुशंसा नहीं की गई है
मैटास फिडेमाइजर

0

ECMAScript 2018 नहीं है?

मेरा लक्ष्य यह था कि हम जो नामांकित समूहों के साथ उपयोग कर रहे हैं, उसी के अनुरूप काम करें। जबकि ECMAScript 2018 में आप ?<groupname>नामांकित समूह को इंगित करने के लिए समूह के अंदर रख सकते हैं, पुराने जावास्क्रिप्ट के लिए मेरे समाधान में, आप (?!=<groupname>)समूह के अंदर उसी कार्य को करने के लिए रख सकते हैं । तो यह कोष्ठक का एक अतिरिक्त सेट और एक अतिरिक्त है !=। बहुत करीब!

मैंने इसे एक स्ट्रिंग प्रोटोटाइप फ़ंक्शन में लपेटा

विशेषताएं

  • पुराने जावास्क्रिप्ट के साथ काम करता है
  • कोई अतिरिक्त कोड नहीं
  • उपयोग करने के लिए बहुत सरल है
  • रेगेक्स अभी भी काम करता है
  • समूह regex के भीतर ही प्रलेखित हैं
  • समूह के नाम में स्थान हो सकते हैं
  • परिणामों के साथ वस्तु लौटाता है

अनुदेश

  • (?!={groupname})प्रत्येक समूह के अंदर जगह जिसे आप नाम देना चाहते हैं
  • किसी भी गैर पर कब्जा समूहों को खत्म करने की याद ()रख कर ?:उस समूह की शुरुआत में। इनका नाम नहीं होगा।

arrays.js

// @@pattern - includes injections of (?!={groupname}) for each group
// @@returns - an object with a property for each group having the group's match as the value 
String.prototype.matchWithGroups = function (pattern) {
  var matches = this.match(pattern);
  return pattern
  // get the pattern as a string
  .toString()
  // suss out the groups
  .match(/<(.+?)>/g)
  // remove the braces
  .map(function(group) {
    return group.match(/<(.+)>/)[1];
  })
  // create an object with a property for each group having the group's match as the value 
  .reduce(function(acc, curr, index, arr) {
    acc[curr] = matches[index + 1];
    return acc;
  }, {});
};    

प्रयोग

function testRegGroups() {
  var s = '123 Main St';
  var pattern = /((?!=<house number>)\d+)\s((?!=<street name>)\w+)\s((?!=<street type>)\w+)/;
  var o = s.matchWithGroups(pattern); // {'house number':"123", 'street name':"Main", 'street type':"St"}
  var j = JSON.stringify(o);
  var housenum = o['house number']; // 123
}

ओ का परिणाम

{
  "house number": "123",
  "street name": "Main",
  "street type": "St"
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.