2011-03-20 84 views
136

據我所知,在JavaScript中沒有命名捕獲組這樣的事情。什麼是獲得類似功能的替代方法?在JavaScript正則表達式中命名捕獲組?

+1

捕獲組在JavaScript是由數.. $ 1是第一個捕捉組,$ 2,$ 3 ...高達$ 99,但它聽起來就像你想要別的東西 - 不存在 – Erik 2011-03-20 08:09:50

+22

@Erik你在談論_numbered_捕獲組,OP在談論_named_捕獲組。它們存在,但我們想知道JS中是否支持它們。 – 2012-11-09 15:41:46

+3

有一個[將JavaScript命名爲正則表達式的提議](https://github.com/littledan/es-regexp-named-groups),但是如果我們這樣做的話,可能需要幾年時間才能看到。 – 2016-10-11 05:12:25

回答

53

ECMAScript 2018將named capturing groups引入到JavaScript正則表達式中。

如果您需要支持較舊的瀏覽器,您可以使用正常(編號)的捕獲組完成任何操作,您可以使用命名捕獲組,只需要跟蹤數字 - 如果在正則表達式變化中捕獲組。

只有兩個「結構」命名的捕獲組的我能想到的優勢:

  1. 在一些正則表達式的口味(.NET和JGSoft,據我所知),您可以使用相同的正則表達式中的不同組的名稱(see here for an example where this matters)。但大多數正則表達式並不支持這個功能。

  2. 如果您需要在被數字包圍的情況下引用編號的捕獲組,您可能會遇到問題。假設您想向零位添加零,因此想要用$10替換(\d)。在JavaScript中,這將起作用(只要您的正則表達式中捕獲組少於10個),但Perl會認爲您正在尋找反向引用號10而不是1,然後是0。在Perl中,在這種情況下可以使用${1}0

除此之外,命名捕獲組只是「語法糖」。它有助於僅在真正需要它們時使用捕獲組,並且在所有其他情況下使用非捕獲組(?:...)

更大的問題(在我看來)使用JavaScript的是,它不支持正則表達式冗長這將使可讀的,複雜的正則表達式輕鬆很多的創作。

Steve Levithan's XRegExp library解決了這些問題。

+5

許多風格允許在正則表達式中多次使用相同的捕獲組名稱。但是,只有.NET和Perl 5.10+才能保持參與比賽的最後一組名稱所捕獲的值,因此特別有用。 – slevithan 2012-06-01 03:36:21

+3

+1爲非捕獲組 – Wulf 2012-08-25 09:50:46

+86

+1的巨大優勢是:您可以更改您的RegExp,不需要數字到變量的映射。非捕獲組解決了這個問題,除了一種情況:**如果組的順序發生變化怎麼辦?**另外,如果把這些額外的字符放在其他組中...... – 2012-11-09 15:45:44

6

命名捕獲組提供了一件事:減少混亂複雜的正則表達式。

這真的取決於你的用例,但也許漂亮 - 打印你的正則表達式可以幫助。

或者你可以嘗試定義常量指捕獲的組。然後

評論也可能有助於向人展示誰看了你的代碼,你做了什麼。

對於剩下的我必須蒂姆斯答案達成一致。

59

您可以使用XRegExp,增強,可擴展,跨瀏覽器的正則表達式,包括額外的語法,標誌的支持,和方法的實現:

  • 添加新的正則表達式和替換文本語法,包括全面支持爲named capture
  • 添加兩個新的正則表達式標誌:s,用於點匹配所有字符(也稱爲dotall或單線模式)和x,用於自由間距和註釋(又名擴展模式)。
  • 提供一套功能和方法,使複雜的正則表達式處理變得輕而易舉。
  • 自動修復了正則表達式行爲和語法中最常遇到的跨瀏覽器不一致問題。
  • 讓您輕鬆創建和使用插件,爲XRegExp的正則表達式語言添加新的語法和標誌。
+4

非常棒!非常感謝。 – 2012-03-31 20:24:42

4

有一個名爲named-regexp,你可以在你的Node.js項目中使用的Node.js庫(在通過包裝與browserify或其他包裝腳本庫中的瀏覽器)。但是,該庫不能用於包含非命名捕獲組的正則表達式。

如果您計算正則表達式中的開始捕獲大括號,您可以在正則表達式中創建命名捕獲組與編號捕獲組之間的映射,並且可以自由組合和匹配。在使用正則表達式之前,您只需刪除組名。我寫了三個函數來證明這一點。看到這個要點:https://gist.github.com/gbirke/2cc2370135b665eee3ef

+0

這是令人驚訝的輕量級,我會試試 – 2016-08-01 05:58:39

+0

它是否適用於複雜正則表達式中常規組內的嵌套命名組? – ElSajko 2016-10-14 22:18:16

+0

這並不完美。錯誤時:getMap(「((a | b(: c)))」); foo應該是第三組,而不是第二組。 /((a|b(c)))/g.exec("bc「); [「bc」,「bc」,「bc」,「c」] – ElSajko 2016-10-14 22:27:54

2

雖然你不能做到這一點香草的JavaScript,也許你可以使用一些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));

+0

這很酷。我只是想..不可能創建一個接受自定義正則表達式的正則表達式函數嗎?所以你可以像'var assocArray = Regex(「hello alex,我是dennis」,「hello({hisName}。+),我是({yourName}。+)」);' – Forivin 2015-08-29 16:46:52

+0

@Forivin顯然你可以進一步發展這個功能。它不會很難得到它的工作:D – 2015-08-29 19:38:37

+0

您可以通過在其原型中添加一個函數來擴展「RegExp」對象。 – 2016-02-16 19:27:16

38

另一種可能的解決方案:創建包含組名稱和索引的對象。

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

然後,使用對象鍵來引用組:

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

這提高了使用正則表達式的結果的代碼的可讀性/質量,但不是正則表達式本身的可讀性。

31

在ES6你可以使用數組解構趕上你的組:

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

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

注意:

  • 在最後let第一個逗號跳過結果數組的第一個值,也就是整個匹配字符串
  • || [].match()之後將防止出現解構錯誤(因爲.match()將返回null
+1

第一個逗號是因爲匹配返回的數組的第一個元素是輸入表達式,對不對? – 2016-07-31 01:04:46

+1

'String.prototype.match'返回一個數組:位置0處的整個匹配字符串,然後是之後的任何組。第一個逗號說「跳過位置0的元素」 – 2016-07-31 06:34:25

+1

我最喜歡的答案是用於那些有transpiling或ES6 +目標的人。這並不一定能夠防止不一致性錯誤以及命名索引,例如,一個重用的正則表達式會發生變化,但我認爲這裏的簡潔很容易彌補這一點。我在'String.prototype.match'上選擇了'RegExp.prototype.exec'作爲字符串可能爲'null'或'undefined'的地方。 – 2017-07-31 15:29:15

2

命名捕獲組可能會很快將其轉換爲JavaScript。
The proposal for it is at stage 3 already.

捕獲組可以使用(?...))語法,對於 任何標識符名稱。日期的正則表達式可以是 ,寫爲/(?\ d {4}) - (?\ d {2}) - (?\ 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';