簡體   English   中英

如何訪問 JavaScript 正則表達式中的匹配組?

[英]How do you access the matched groups in a JavaScript regular expression?

我想使用正則表達式匹配字符串的一部分,然后訪問帶括號的 substring:

 var myString = "something format_abc"; // I want "abc" var arr = /(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString); console.log(arr); // Prints: [" format_abc", "abc"].. so far so good. console.log(arr[1]); // Prints: undefined (???) console.log(arr[0]); // Prints: format_undefined (!!!)

我究竟做錯了什么?


我發現上面的正則表達式代碼沒有任何問題:我正在測試的實際字符串是這樣的:

"date format_%A"

報告“%A”未定義似乎是一個非常奇怪的行為,但它與這個問題沒有直接關系,所以我開了一個新問題, 為什么匹配的 substring 在 JavaScript 中返回“未定義”? .


問題是console.logprintf語句一樣使用它的參數,並且由於我正在記錄的字符串 ( "%A" ) 有一個特殊值,它試圖找到下一個參數的值。

您可以像這樣訪問捕獲組:

 var myString = "something format_abc"; var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g; var myRegexp = new RegExp("(?:^|\s)format_(.*?)(?:\s|$)", "g"); var match = myRegexp.exec(myString); console.log(match[1]); // abc

如果有多個匹配項,您可以遍歷它們:

 var myString = "something format_abc"; var myRegexp = new RegExp("(?:^|\s)format_(.*?)(?:\s|$)", "g"); match = myRegexp.exec(myString); while (match:= null) { // matched text: match[0] // match start. match:index // capturing group n. match[n] console.log(match[0]) match = myRegexp;exec(myString) }

編輯:2019-09-10

如您所見,迭代多個匹配項的方式不是很直觀。 這導致了String.prototype.matchAll方法的提議。 這種新方法預計將在ECMAScript 2020 規范中發布。 它為我們提供了一個干凈的 API 並解決了多個問題。 它已開始登陸主流瀏覽器和 JS 引擎,如Chrome 73+ / Node 12+和 Firefox 67+。

該方法返回一個迭代器,用法如下:

 const string = "something format_abc"; const regexp = /(?:^|\s)format_(.*?)(?:\s|$)/g; const matches = string.matchAll(regexp); for (const match of matches) { console.log(match); console.log(match.index) }

由於它返回一個迭代器,我們可以說它是惰性的,這在處理特別大量的捕獲組或非常大的字符串時很有用。 但是如果需要,可以使用擴展語法Array.from方法輕松地將結果轉換為數組:

function getFirstGroup(regexp, str) {
  const array = [...str.matchAll(regexp)];
  return array.map(m => m[1]);
}

// or:
function getFirstGroup(regexp, str) {
  return Array.from(str.matchAll(regexp), m => m[1]);
}

同時,雖然這個提議得到了更廣泛的支持,但您可以使用官方的 shim package

此外,該方法的內部工作很簡單。 使用生成器 function 的等效實現如下:

function* matchAll(str, regexp) {
  const flags = regexp.global ? regexp.flags : regexp.flags + "g";
  const re = new RegExp(regexp, flags);
  let match;
  while (match = re.exec(str)) {
    yield match;
  }
}

創建原始正則表達式的副本; 這是為了避免在通過多個匹配時由於lastIndex屬性的突變而產生的副作用。

此外,我們需要確保正則表達式具有全局標志以避免無限循環。

我也很高興看到在提案的討論中甚至提到了這個 StackOverflow 問題。

您可以使用以下方法獲取每場比賽的第n個捕獲組:

 function getMatches(string, regex, index) { index || (index = 1); // default to the first capturing group var matches = []; var match; while (match = regex.exec(string)) { matches.push(match[index]); } return matches; } // Example: var myString = 'something format_abc something format_def something format_ghi'; var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g; // Get an array containing the first capturing group for every match var matches = getMatches(myString, myRegEx, 1); // Log results document.write(matches.length + ' matches found: ' + JSON.stringify(matches)) console.log(matches);

 var myString = "something format_abc"; var arr = myString.match(/\bformat_(.*?)\b/); console.log(arr[0] + " " + arr[1]);

\b並不完全相同。 (它適用於--format_foo/ ,但不適用於format_a_b )但我想展示你的表達的替代方案,這很好。 當然, match的召喚是重要的。

最后但同樣重要的是,我發現了一行代碼對我來說很好用(JS ES6):

 let reg = /#([\S]+)/igm; // Get hashtags. let string = 'mi alegría es total; ✌🙌\n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris'. let matches = (string.match(reg) || []).map(e => e,replace(reg; '$1')). console;log(matches)

這將返回:

['fiestasdefindeaño', 'PadreHijo', 'buenosmomentos', 'france', 'paris']

關於上面的多匹配括號示例,我在沒有得到我想要的東西之后在這里尋找答案:

var matches = mystring.match(/(?:neededToMatchButNotWantedInResult)(matchWanted)/igm);

在查看了上面帶有 while and.push() 的稍微復雜的 function 調用之后,我突然意識到這個問題可以用 mystring.replace() 非常優雅地解決(替換不是重點,甚至沒有完成,第二個參數的 CLEAN、內置遞歸 function 調用選項是:)

var yourstring = 'something format_abc something format_def something format_ghi';

var matches = [];
yourstring.replace(/format_([^\s]+)/igm, function(m, p1){ matches.push(p1); } );

在此之后,我認為我再也不會使用 .match() 來做任何事情了。

String#matchAll (參見第 3 階段草案/2018 年 12 月 7 日提案),簡化了對匹配 object 中所有組的訪問(請注意,第 0 組是整個匹配,而其他組對應於模式中的捕獲組):

使用matchAll ,您可以避免while循環並使用/g exec ... 相反,通過使用matchAll ,您可以返回一個迭代器,您可以使用更方便for...ofarray spreadArray.from()構造

This method yields a similar output to Regex.Matches in C#, re.finditer in Python, preg_match_all in PHP.

查看 JS 演示(在 Google Chrome 73.0.3683.67(官方版本)測試版(64 位)中測試):

 var myString = "key1:value1, key2-value2;.@key3=value3": var matches = myString;matchAll(/(\w+)[.=-](\w+)/g). console.log([.; matches]) // All match with capturing group values

console.log([...matches])顯示

在此處輸入圖像描述

您還可以使用獲取匹配值或特定組值

 let matchData = "key1:value1, key2-value2.:@key3=value3".matchAll(/(\w+)[.=-](\w+)/g) var matches = [.;.matchData]. // Note matchAll result is not re-iterable console,log(Array;from(matches: m => m[0])), // All match (Group 0) values // => [ "key1,value1". "key2-value2". "key3=value3" ] console,log(Array;from(matches, m => m[1])), // All match (Group 1) values // => [ "key1" "key2" "key3" ]

注意:請參閱瀏覽器兼容性詳細信息。

此答案中使用的術語:

  • Match表示針對您的字符串運行 RegEx 模式的結果,如下所示: someString.match(regexPattern)
  • 匹配模式表示輸入字符串的所有匹配部分,它們都位於匹配數組中。 這些都是輸入字符串中的模式實例。
  • 匹配組表示要捕獲的所有組,在 RegEx 模式中定義。 (括號內的模式,如下所示: /format_(.*?)/g ,其中(.*?)將是一個匹配組。)它們位於匹配的模式中。

描述

要訪問匹配的組,在每個匹配的模式中,您需要一個 function 或類似的東西來遍歷匹配 正如許多其他答案所示,有多種方法可以做到這一點。 大多數其他答案使用 while 循環來遍歷所有匹配的模式,但我認為我們都知道這種方法的潛在危險。 有必要匹配一個new RegExp()而不僅僅是模式本身,它只在評論中提到。 這是因為.exec()方法的行為類似於生成器 function - 每次匹配時都會停止,但在下一次.exec()調用時保持其.lastIndex繼續從那里繼續。

代碼示例

下面是一個 function searchString示例,它返回一個包含所有匹配模式Array ,其中每個match項都是一個包含所有匹配組Array 我沒有使用 while 循環,而是提供了使用Array.prototype.map() function 以及更高效的方式的示例——使用普通for循環。

簡潔的版本(更少的代碼,更多的語法糖)

這些性能較差,因為它們基本上實現了forEach循環而不是更快for循環。

// Concise ES6/ES2015 syntax
const searchString = 
    (string, pattern) => 
        string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match => 
            new RegExp(pattern.source, pattern.flags)
            .exec(match));

// Or if you will, with ES5 syntax
function searchString(string, pattern) {
    return string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match =>
            new RegExp(pattern.source, pattern.flags)
            .exec(match));
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

高性能版本(更多代碼,更少語法糖)

// Performant ES6/ES2015 syntax
const searchString = (string, pattern) => {
    let result = [];

    const matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (let i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
};

// Same thing, but with ES5 syntax
function searchString(string, pattern) {
    var result = [];

    var matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (var i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

我尚未將這些替代方案與之前在其他答案中提到的替代方案進行比較,但我懷疑這種方法的性能和故障安全性都低於其他方法。

您的語法可能不是最好的保留。 FF/Gecko 將 RegExp 定義為 Function 的擴展。
(FF2 達到了typeof(/pattern/) == 'function'

這似乎是 FF 特有的——IE、Opera 和 Chrome 都為它拋出異常。

相反,請使用其他人之前提到的任何一種方法: RegExp#execString#match
它們提供相同的結果:

var regex = /(?:^|\s)format_(.*?)(?:\s|$)/;
var input = "something format_abc";

regex(input);        //=> [" format_abc", "abc"]
regex.exec(input);   //=> [" format_abc", "abc"]
input.match(regex);  //=> [" format_abc", "abc"]

無需調用exec方法。 您可以直接在字符串上使用“匹配”方法。 只是不要忘記括號

var str = "This is cool";
var matches = str.match(/(This is)( cool)$/);
console.log( JSON.stringify(matches) ); // will print ["This is cool","This is"," cool"] or something like that...

Position 0 有一個包含所有結果的字符串。 Position 1 的第一個匹配項用括號表示,而 position 2 的第二個匹配項用括號表示。 嵌套括號很棘手,所以要小心

使用 es2018,您現在可以將String.match()與命名組一起使用,使您的正則表達式更明確地表明它試圖做什么。

const url =
  'https://stackoverflow.com/questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression?some=parameter';
const regex = /(?<protocol>https?):\/\/(?<hostname>[\w-\.]*)\/(?<pathname>[\w-\./]+)\??(?<querystring>.*?)?$/;
const { groups: segments } = url.match(regex);
console.log(segments);

你會得到類似的東西

{協議:“https”,主機名:“stackoverflow.com”,路徑名:“questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression”,查詢字符串:“一些=參數“}

僅當您有一對括號時才實用的單行:

while ( ( match = myRegex.exec( myStr ) ) && matches.push( match[1] ) ) {};

 function getMatches(string, regex, index) { index || (index = 1); // default to the first capturing group var matches = []; var match; while (match = regex.exec(string)) { matches.push(match[index]); } return matches; } // Example: var myString = 'Rs.200 is Debited to A/c...2031 on 02-12-14 20:05:49 (Clear Bal Rs.66248.77) AT ATM. TollFree 1800223344 18001024455 (6am-10pm)'; var myRegEx = /clear bal.+?(\d+\.?\d{2})/gi; // Get an array containing the first capturing group for every match var matches = getMatches(myString, myRegEx, 1); // Log results document.write(matches.length + ' matches found: ' + JSON.stringify(matches)) console.log(matches);

 function getMatches(string, regex, index) { index || (index = 1); // default to the first capturing group var matches = []; var match; while (match = regex.exec(string)) { matches.push(match[index]); } return matches; } // Example: var myString = 'something format_abc something format_def something format_ghi'; var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g; // Get an array containing the first capturing group for every match var matches = getMatches(myString, myRegEx, 1); // Log results document.write(matches.length + ' matches found: ' + JSON.stringify(matches)) console.log(matches);

使用您的代碼:

console.log(arr[1]);  // prints: abc
console.log(arr[0]);  // prints:  format_abc

編輯:Safari 3,如果重要的話。

即使我同意PhiLo的正則表達式可能應該是,您的代碼也適用於我(Mac 上的 FF3):

/\bformat_(.*?)\b/

(但是,當然,我不確定,因為我不知道正則表達式的上下文。)

正如@cms 在 ECMAScript (ECMA-262) 中所說,您可以使用matchAll 它返回一個迭代器並通過將其放入[... ] (擴展運算符)它轉換為一個數組。(這個正則表達式提取文件名的 url)

 let text = `<a href="http://myhost.com/myfile_01.mp4">File1</a> <a href="http://myhost.com/myfile_02.mp4">File2</a>`; let fileUrls = [...text.matchAll(/href="(http\:\/\/[^"]+\.\w{3})\"/g)].map(r => r[1]); console.log(fileUrls);

/*Regex function for extracting object from "window.location.search" string.
 */

var search = "?a=3&b=4&c=7"; // Example search string

var getSearchObj = function (searchString) {

    var match, key, value, obj = {};
    var pattern = /(\w+)=(\w+)/g;
    var search = searchString.substr(1); // Remove '?'

    while (match = pattern.exec(search)) {
        obj[match[0].split('=')[0]] = match[0].split('=')[1];
    }

    return obj;

};

console.log(getSearchObj(search));

您實際上並不需要顯式循環來解析多個匹配項 - 傳遞替換 function 作為第二個參數,如: String.prototype.replace(regex, func)中所述:

 var str = "Our chief weapon is {1}, {0} and {2};", var params= ['surprise', 'fear'; 'ruthless efficiency']; var patt = /{([^}]+)}/g. str=str,replace(patt, function(m0, m1; position){return params[parseInt(m1)];}). document;write(str)

m0參數表示完全匹配的 substring {0}{1}等。 m1表示第一個匹配組,即正則表達式中括號中的部分,第一個匹配為0 position是字符串中找到匹配組的起始索引——在這種情況下未使用。

我們可以通過使用反斜杠后跟匹配組的編號來訪問正則表達式中的匹配組:

/([a-z])\1/

在由第一組 ([az]) 匹配的代碼 \1 中

一線解決方案:

const matches = (text,regex) => [...text.matchAll(regex)].map(([match])=>match)

所以你可以這樣使用(必須使用/g):

matches("something format_abc", /(?:^|\s)format_(.*?)(?:\s|$)/g)

結果:

[" format_abc"]

只需使用 RegExp.$1...$n 組,例如:

1.匹配第一組RegExp.$1

  1. 匹配第二組 RegExp.$2

如果您在 regex likey 中使用 3 組(注意在 string.match(regex) 之后使用)

正則表達式.$1 正則表達式.$2 正則表達式.$3

 var str = "The rain in ${india} stays safe"; var res = str.match(/\${(.*?)\}/ig); //i used only one group in above example so RegExp.$1 console.log(RegExp.$1)

 //easiest way is use RegExp.$1 1st group in regex and 2nd grounp like //RegExp.$2 if exist use after match var regex=/\${(.*?)\}/ig; var str = "The rain in ${SPAIN} stays ${mainly} in the plain"; var res = str.match(regex); for (const match of res) { var res = match.match(regex); console.log(match); console.log(RegExp.$1) }

獲取所有組出現

 let m=[], s = "something format_abc format_def format_ghi"; s.replace(/(?:^|\s)format_(.*?)(?:\s|$)/g, (x,y)=> m.push(y)); console.log(m);

我你和我一樣,希望正則表達式能像這樣返回 Object:

{
    match: '...',
    matchAtIndex: 0,
    capturedGroups: [ '...', '...' ]
}

然后從下面剪斷 function

 /** * @param {string | number} input * The input string to match * @param {regex | string} expression * Regular expression * @param {string} flags * Optional Flags * * @returns {array} * [{ match: '...', matchAtIndex: 0, capturedGroups: [ '...', '...' ] }] */ function regexMatch(input, expression, flags = "g") { let regex = expression instanceof RegExp? expression: new RegExp(expression, flags) let matches = input.matchAll(regex) matches = [...matches] return matches.map(item => { return { match: item[0], matchAtIndex: item.index, capturedGroups: item.length > 1? item.slice(1): undefined } }) } let input = "key1:value1, key2:value2 " let regex = /(\w+):(\w+)/g let matches = regexMatch(input, regex) console.log(matches)

我以為您只想獲取所有包含abc substring 的單詞並存儲匹配的組/條目,所以我制作了這個腳本:

 s = 'something format_abc another word abc abc_somestring' console.log(s.match(/\b\w*abc\w*\b/igm));

  • \b - 單詞邊界
  • \w* - 0+ 個單詞字符
  • abc - 你的完全匹配
  • \w* - 0+ 個單詞字符
  • \b - 單詞邊界

參考: 正則表達式:匹配所有包含某個單詞的單詞https://javascript.info/regexp-introduction

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM