JavaScript正規表現のキャプチャグループに名前を付けましたか?


208

私の知る限りでは、JavaScriptの名前付きキャプチャグループなどはありません。同様の機能を取得する別の方法は何ですか?


1
JavaScriptのキャプチャグループは番号順です。$ 1は最初にキャプチャされたグループです。$ 2、$ 3 ... $ 99までですが、何か他に必要なものがあるようです-これは存在しません
Erik

24
@Erikは番号付きのキャプチャグループについて話している、OPは名前付きのキャプチャグループについて話している。それらは存在しますが、JSでそれらがサポートされているかどうかを知りたいです。
アルバメンデス

4
名前付き正規表現をJavaScriptに導入するという提案がありますが、それが実現するまでに数年かかる可能性があります。
fregante 2016年

Firefoxは、Webサイトで名前付きキャプチャグループを使用しようとしたことで私を罰しました...本当に私自身の責任です。stackoverflow.com/a/58221254/782034
Nick Grealy

回答:


134

ECMAScript 2018では、名前付きキャプチャグループをJavaScript正規表現に導入しています。

例:

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

古いブラウザをサポートする必要がある場合は、名前付きキャプチャグループで実行できる通常の(番号付き)キャプチャグループですべてを実行できます。番号を追跡するだけで済みます。これは、キャプチャグループの順序が正規表現の変更。

私が考えることができる名前付きキャプチャグループの2つの「構造的」利点のみがあります。

  1. 一部の正規表現フレーバー(私が知る限り、.NETおよびJGSoft)では、正規表現の異なるグループに同じ名前を使用できます(これが重要な例については、こちらを参照してください)。しかし、ほとんどの正規表現フレーバーは、とにかくこの機能をサポートしていません。

  2. 数字で囲まれている状況で番号付きのキャプチャグループを参照する必要がある場合は、問題が発生する可能性があります。あなたが数字にゼロを追加したいので、交換したいとしましょう(\d)$10。JavaScriptではこれが機能します(正規表現のキャプチャグループが10未満の場合)。ただし、Perlは数値では10なく後方参照番号を探し1、その後に0。Perlでは${1}0、この場合に使用できます。

それ以外では、名前付きのキャプチャグループは単なる「構文上の砂糖」です。本当に必要な場合にのみキャプチャグループを使用し、非キャプチャグループを使用すると役立ちます(?:...)し、他のすべての状況で。

JavaScriptに関する(私の意見では)より大きな問題は、JavaScriptが冗長な正規表現をサポートしていないため、読みやすく複雑な正規表現を簡単に作成できることです。

Steve LevithanのXRegExpライブラリはこれらの問題を解決します。


5
多くのフレーバーでは、正規表現で同じキャプチャグループ名を複数回使用できます。しかし、.NETとPerl 5.10+のみが、一致に参加した名前の最後のグループによってキャプチャされた値を保持することによってこれを特に有用にします。
2012年

103
大きな利点は、RegExpを変更するだけで、数値から変数へのマッピングができないことです。非キャプチャグループはこの問題を解決しますが、1つの場合を除きます。グループの順序が変わった場合はどうなりますか?また、この余分な文字を他のグループに配置することは不愉快です...
アルバメンデス

55
いわゆるシンタックスシュガー 、コードを読みやすくするのに役立ちます。
Mrchief 2013

1
名前付きのキャプチャグループには、本当に価値のあるもう1つの理由があると思います。たとえば、正規表現を使用して文字列から日付を解析する場合、値と正規表現を取得する柔軟な関数を作成できます。正規表現が年、月、日付のキャプチャに名前を付けている限り、最小限のコードで正規表現の配列を実行できます。
Dewey Vozel 2016年

4
2019年10月の時点で、Firefox、IE 11、およびMicrosoft Edge(Chromium以前)は名前付きグループキャプチャをサポートしていません。他のほとんどのブラウザ(OperaやSamsungモバイルも含む)がサポートしています。caniuse.com/...
JDBはまだモニカ覚えて

63

追加の構文、フラグ、およびメソッドのサポートを含む、正規表現の拡張された拡張可能なクロスブラウザー実装であるXRegExpを使用できます。

  • 名前付きキャプチャの包括的なサポートを含む、新しい正規表現と置換テキスト構文を追加します
  • 2つの新しい正規表現フラグを追加:s、ドットをすべての文字に一致させる(別名dotallまたはシングルラインモード)、および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()一致がないとき(ので、非構造エラーを防ぐことができます.exec()返されますnull

1
最初のコンマは、matchによって返される配列の最初の要素が入力式であるためです。
エミリオグリソリア2016

1
String.prototype.match配列を返します。位置0の一致した文字列全体、その後のグループ。最初のカンマは「0の位置で要素をスキップ」と言います
fregante

2
トランスパイルまたはES6 +ターゲットを使用している人のためにここで私のお気に入りの答え。これは必ずしも不整合エラーを防ぐわけではなく、たとえば再利用された正規表現が変更された場合に名前付きインデックスが防ぐことができますが、ここでの簡潔さはそれを簡単に補うと思います。私が選んだのためにきたRegExp.prototype.exec以上のString.prototype.match文字列がかもしれ場所でnullundefined
マイク・ヒル

22

更新:ようやくJavaScript(ECMAScript 2018)になりました!


名前付きのキャプチャグループは、すぐにJavaScriptになる可能性があります。
その提案はすでにステージ3です。

キャプチャグループには(?<name>...)、識別子を使用して、構文を使用して山括弧内に名前を付けることができます。日付の正規表現は、のように書くことができます/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u。各名前は一意であり、ECMAScript IdentifierNameの文法に従う必要があります。

名前付きグループには、正規表現結果のgroupsプロパティのプロパティからアクセスできます。名前のないグループと同様に、グループへの番号付き参照も作成されます。例えば:

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の提案です。
GOTO 0

もしあなたが'18を使用しているなら、破壊と一緒にオールインするかもしれません。let {year, month, day} = ((result) => ((result) ? result.groups : {}))(re.exec('2015-01-02'));
ハッシュブラウン

6

キャプチャされたグループに名前を付けると、複雑な正規表現との混乱が少なくなります。

それは本当にあなたのユースケースに依存しますが、多分あなたの正規表現をきれいに印刷することが役立つかもしれません。

または、キャプチャしたグループを参照するための定数を定義することもできます。

コメントは、あなたのコードを読んだ他の人、あなたがしたことを示すのにも役立ちます。

残りのために私はティムスの答えに同意する必要があります。


5

named-regexpという node.jsライブラリがあります。node.jsプロジェクトで使用できるます(ブラウザで、browserifyまたは他のパッケージスクリプトを使用してライブラリをパッケージ化します)。ただし、ライブラリは、名前のないキャプチャグループを含む正規表現では使用できません。

正規表現で開始キャプチャ中括弧を数える場合、名前付きキャプチャグループと正規表現の番号付きキャプチャグループ間のマッピングを作成でき、自由に組み合わせて一致させることができます。正規表現を使用する前に、グループ名を削除する必要があります。それを示す3つの関数を作成しました。この要旨を参照してください:https : //gist.github.com/gbirke/2cc2370135b665eee3ef


それは驚くほど軽量です。試してみることにします
fregante

複雑な正規表現の正規グループ内のネストされた名前付きグループで動作しますか?
ElSajko 2016年

それは完璧ではありません。バグ発生時:getMap( "((a | b(:<foo> c)))"); fooは2番目ではなく3番目のグループでなければなりません。/((a|b(c)))/g.exec("bc "); ["bc"、 "bc"、 "bc"、 "c"]
ElSajko

3

ティムPietzckerは、 JavaScriptの正規表現にグループを取り込むという名前のECMAScript 2018が導入しました。しかし、上記の答えで私が見つけなかったのは、正規表現自体で名前付きのキャプチャされたグループを使用する方法でした。

次の構文で名前付きのキャプチャグループを使用できます\k<name>。例えば

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

また、Forivinが言ったように、次のようにオブジェクトの結果でキャプチャグループを使用できます。

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

バニラ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));


それはいいね。私はただ考えているだけです。カスタムの正規表現を受け入れる正規表現関数を作成することはできませんか?あなたがそうすることができるようにvar assocArray = Regex("hello alex, I am dennis", "hello ({hisName}.+), I am ({yourName}.+)");
フォーヴィン

@Forivin明らかに、さらに進んでこの機能を開発できます。それは作業を取得するのは難しいことではないでしょう:D
マティアスFidemraizer

RegExpプロトタイプに関数を追加することにより、オブジェクトを拡張できます。
TA氏2016

Mr.TA私の知る限り、@、組み込まれて拡張するためのオブジェクトお勧めしません
マティアスFidemraizer

0

ECMAScript 2018を持っていないのですか?

私の目標は、名前付きグループで慣れているものと可能な限り同じように機能させることでした。ECMAScript 2018 ?<groupname>では、グループ内に配置して名前付きグループを示すことができますが、私の古いJavaScriptのソリューションで(?!=<groupname>)は、グループ内に配置して同じことを行うことができます。つまり、括弧の追加セットと追加の!=です。非常に近いです!

すべてを文字列プロトタイプ関数にラップしました

特徴

  • 古いJavaScriptで動作します
  • 余分なコードはありません
  • 使い方はかなり簡単
  • 正規表現はまだ機能します
  • グループは正規表現自体に文書化されています
  • グループ名にはスペースを含めることができます
  • 結果を持つオブジェクトを返します

指示

  • 場所(?!={groupname})の名前にしたい各グループ内
  • そのグループの先頭に()置くことによって、非キャプチャグループを削除することを忘れないでください?:。これらは名前が付けられません。

array.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
}

oの結果

{
  "house number": "123",
  "street name": "Main",
  "street type": "St"
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.