通常の延長

RegExp コンストラクター

ES5 では、RegExp コンストラクターのパラメーターには 2 つの状況があります。

最初のケースはパラメーターが文字列である場合で、この場合 2 番目のパラメーターは正規表現の修飾子 (フラグ) を表します。

var regex = new RegExp('xyz', 'i');
// と同等
var regex = /xyz/i;

2 番目のケースはパラメータが正規表現である場合で、この場合は元の正規表現のコピーが返されます。

var regex = 新しい RegExp(/xyz/i);
// と同等
var regex = /xyz/i;

ただし、ES5 では、現時点では 2 番目のパラメーターを使用して修飾子を追加することはできません。使用しないと、エラーが報告されます。

var regex = new RegExp(/xyz/, 'i');
// Uncaught TypeError: ある RegExp を別の RegExp から構築するときにフラグを指定できません

ES6 ではこの動作が変更されます。 RegExp コンストラクターの最初のパラメーターが通常のオブジェクトの場合、2 番目のパラメーターを使用して修飾子を指定できます。さらに、返された正規表現は、元の正規表現の修飾子を無視し、新しく指定された修飾子のみを使用します。

新しい RegExp(/abc/ig, 'i').flags
// "私"

上記のコードでは、元の通常のオブジェクトの修飾子は ig ですが、これは 2 番目のパラメーター i によって上書きされます。

文字列の通常のメソッド

ES6 が登場する前は、文字列オブジェクトには正規表現を使用できる match()replace()search()split() という合計 4 つのメソッドがありました。

ES6 は、言語内の RegExp インスタンス メソッドの 4 つのメソッドすべてを呼び出すため、すべての規則性関連のメソッドは RegExp オブジェクトで定義されます。

  • String.prototype.matchRegExp.prototype[Symbol.match] を呼び出します
  • String.prototype.replaceRegExp.prototype[Symbol.replace] を呼び出します
  • String.prototype.searchRegExp.prototype[Symbol.search] を呼び出します
  • String.prototype.splitRegExp.prototype[Symbol.split] を呼び出します

u 修飾子

ES6 は正規表現に「u」修飾子を追加します。これは「Unicode モード」を意味し、「\uFFFF」より大きい Unicode 文字を正しく処理するために使用されます。つまり、4 バイトの UTF-16 エンコーディングが正しく処理されます。

/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true

上記のコードでは、\uD83D\uDC2A は 4 バイトの UTF-16 エンコーディングであり、文字を表します。ただし、ES5 は 4 バイトの UTF-16 エンコーディングをサポートしておらず、これを 2 文字として認識するため、コードの 2 行目の結果が「true」になります。 「u」修飾子を追加すると、ES6 はそれを文字として認識するため、コードの最初の行の結果は「false」になります。

「u」修飾子を追加すると、次の正規表現の動作が変更されます。

(1) ドット文字

正規表現におけるドット (.) 文字は、改行文字を除く任意の 1 文字を意味します。コード ポイントが「0xFFFF」より大きい Unicode 文字の場合、ドット文字は認識できないため、「u」修飾子を追加する必要があります。

var s = '𠮷';

/^.$/.test(s) // false
/^.$/u.test(s) // true

上記のコードは、「u」修飾子が追加されていない場合、正規表現は文字列を 2 文字とみなし、一致が失敗することを示しています。

(2)Unicode 文字表現

ES6 では、Unicode 文字を表すために中括弧の使用が追加されています。この表現は、中括弧を認識するために正規表現に「u」修飾子を追加する必要があります。そうしないと、修飾子として解釈されます。

/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true

上記のコードは、u 修飾子が追加されていない場合、正規表現は表現 \u{61} を認識できず、それが 61 個の連続する u に一致するとのみみなされることを示しています。

(3)数量子

u 修飾子を使用すると、すべての量指定子が 0xFFFF より大きいコード ポイントを持つ Unicode 文字を正しく認識するようになります。

/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true

(4)事前定義モード

「u」修飾子は、「0xFFFF」より大きいコードポイントを持つ Unicode 文字を正しく認識できるかどうかという事前定義モードにも影響します。

/^\S$/.test('𠮷') // false
/^\S$/u.test('𠮷') // true

上記のコードの \S は、空白以外のすべての文字に一致する事前定義されたパターンです。 「u」修飾子を追加することによってのみ、「0xFFFF」より大きいコード ポイントを持つ Unicode 文字を正しく照合できます。

これを使用すると、文字列の長さを正しく返す関数を作成できます。

関数 codePointLength(text) {
  var result = text.match(/[\s\S]/gu);
  結果を返しますか? result.length : 0;
}

var s = '𠮷𠮷';

s.length // 4
codePointLength(s) // 2

(5)i 修飾子

一部の Unicode 文字はエンコーディングが異なりますが、フォントは非常に似ています。たとえば、\u004B\u212A はどちらも大文字の K です。

/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true

上記のコードでは、非標準の K 文字は u 修飾子を追加しないと認識できません。

(6) 逃げる

u 修飾子がないと、正規表現で定義されていないエスケープ (カンマ エスケープ \, など) は無効となり、u モードでエラーが報告されます。

/\,/ // /\,/
/\,/u // エラーレポート

上記のコードでは、「u」修飾子がない場合、カンマの前のバックスラッシュは無効です。「u」修飾子を追加すると、エラーが報告されます。

RegExp.prototype.unicode プロパティ

新しい unicode 属性が通常のインスタンス オブジェクトに追加され、u 修飾子が設定されているかどうかを示します。

const r1 = /hello/;
const r2 = /hello/u;

r1.unicode // false
r2.unicode // true

上記のコードでは、正規表現に u 修飾子が設定されているかどうかは、unicode 属性から確認できます。

y 修飾子

ES6 では、「u」修飾子に加えて、「sticky」修飾子と呼ばれる「y」修飾子も正規表現に追加されています。

「y」修飾子の機能は「g」修飾子の機能と似ていますが、これもグローバル一致であり、前の一致が成功した次の位置から開始されます。違いは、「g」修飾子は残りの位置での一致のみを必要とするのに対し、「y」修飾子は一致が最初の残りの位置から開始することを保証することです。これは「接着」の意味です。

var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;

r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"]
r2.exec(s) // null

上記のコードには 2 つの正規表現があり、1 つは g 修飾子を使用し、もう 1 つは y 修飾子を使用します。これら 2 つの正規表現はそれぞれ 2 回実行され、最初に実行されるときはどちらも同じように動作し、残りの文字列は _aa_a になります。 g 修飾子には位置の要件がないため、2 回目の実行では結果が返されますが、y 修飾子は一致が先頭から開始する必要があるため、null が返されます。

ヘッダーが毎回一致するように正規表現を変更すると、「y」修飾子が結果を返します。

var s = 'aaa_aa_a';
var r = /a+_/y;

r.exec(s) // ["aaa_"]
r.exec(s) // ["aa_"]

上記のコードが一致するたびに、残りの文字列の先頭から開始されます。

y 修飾子は、lastIndex 属性を使用するとより適切に説明できます。

const REGEX = /a/g;

// 一致する位置 2 (y) から開始することを指定します
REGEX.lastIndex = 2;

// マッチング成功
const match = REGEX.exec('xaya');

// 位置 3 で正常に一致しました
match.index // 3

// 次の試合は 4 位から始まります
REGEX.lastIndex // 4

//位置 4 から始まる一致に失敗しました
REGEX.exec('xaya') // null

上記のコードでは、「lastIndex」属性は各検索の開始位置を指定し、「g」修飾子は一致するものが見つかるまでこの位置から逆方向に検索します。

y 修飾子も lastIndex 属性を尊重しますが、lastIndex で指定された位置で一致が見つかる必要があります。

const REGEX = /a/y;

// 一致する位置 2 から開始することを指定します
REGEX.lastIndex = 2;

// 接着されていないため、マッチングに失敗しました
REGEX.exec('xaya') // null

// 一致する位置 3 から開始するように指定します
REGEX.lastIndex = 3;

// 位置 3 は接着であり、一致は成功します。
const match = REGEX.exec('xaya');
match.index // 3
REGEX.lastIndex // 4

実際、「y」修飾子はヘッダー一致フラグ「^」を意味します。

/b/y.exec('aba')
// null

上記のコードはヘッダーが一致することを保証できないため、「null」を返します。 y 修飾子の本来の設計意図は、ヘッダー マッチング フラグ ^ をグローバル マッチングで有効にすることです。

以下は文字列オブジェクトの「replace」メソッドの例です。

const REGEX = /a/gy;
'aaxa'.replace(REGEX, '-') // '--xa'

上記のコードでは、最後の「a」は次の一致の先頭に現れないため、置換されません。

「match」メソッドの単一の「y」修飾子は最初の一致のみを返すことができ、すべての一致を返すには「g」修飾子と組み合わせて使用​​する必要があります。

'a1a2a3'.match(/a\d/y) // ["a1"]
'a1a2a3'.match(/a\d/gy) ​​// ["a1", "a2", "a3"]

「y」修飾子の応用例の 1 つは、文字列からトークンを抽出することです。「y」修飾子は、一致の間に欠落文字がないことを保証します。

const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
const TOKEN_G = /\s*(\+|[0-9]+)\s*/g;

tokenize(TOKEN_Y, '3 + 4')
// [ '3', '+', '4' ]
tokenize(TOKEN_G, '3 + 4')
// [ '3', '+', '4' ]

関数 tokenize(TOKEN_REGEX, str) {
  結果 = []; とします。
  一致させてください。
  while (match = TOKEN_REGEX.exec(str)) {
    result.push(一致[1]);
  }
  結果を返します。
}

上記のコードでは、文字列中に不正な文字が含まれていない場合、「y」修飾子と「g」修飾子の抽出結果は同じになります。ただし、不正な文字が出現すると、両者の動作は異なります。

tokenize(TOKEN_Y, '3x + 4')
// [ '3' ]
tokenize(TOKEN_G, '3x + 4')
// [ '3', '+', '4' ]

上記のコードでは、「g」修飾子は不正な文字を無視しますが、「y」修飾子は無視しないため、エラーを見つけやすくなります。

RegExp.prototype.sticky プロパティ

「y」修飾子に一致して、ES6 の通常インスタンス オブジェクトには、「y」修飾子が設定されているかどうかを示す「sticky」属性があります。

var r = /hello\d/y;
r.sticky // true

RegExp.prototype.flags プロパティ

ES6 では、正規表現に新しい flags 属性が追加され、正規表現の修飾子が返されます。

// ES5 ソース属性
// 正規表現のテキストを返します
/abc/ig.ソース
// "ABC"

// ES6 フラグ属性
//正規表現の修飾子を返す
/abc/ig.flags
//「ギ」

s 修飾子: dotAll モード

正規表現では、ドット (.) は任意の 1 文字を表す特殊文字ですが、2 つの例外があります。 1 つは 4 バイトの UTF-16 文字で、「u」修飾子で解決できます。もう 1 つは行終端文字です。

いわゆるラインターミネータは、行の終わりを示す文字です。次の 4 文字は「行末文字」です。

  • U+000A 改行文字 (\n)
  • U+000D キャリッジ リターン (\r)
  • U+2028 行区切り文字
  • U+2029 段落区切り文字 (段落区切り文字)
/foo.bar/.test('foo\nbar')
// 間違い

上記のコードでは、.\n に一致しないため、正規表現は false を返します。

ただし、多くの場合、任意の 1 文字に一致する必要があります。この場合、別の記述方法があります。

/foo[^]bar/.test('foo\nbar')
// 真実

結局のところ、このソリューションは ES2018 導入s 修飾子なので、. は任意の 1 文字と一致します。

/foo.bar/s.test('foo\nbar') // true

これは「dotAll」モードと呼ばれ、ドットがすべての文字を表します。したがって、正規表現には dotAll 属性も導入され、正規表現が dotAll モードであるかどうかを示すブール値を返します。

const re = /foo.bar/s;
// 別の書き方
// const re = new RegExp('foo.bar', 's');

re.test('foo\nbar') // true
re.dotAll // true
re.flags // 's'

/s 修飾子は、複数行の修飾子 /m と競合しません。これらを一緒に使用すると、. はすべての文字に一致し、^$ は各行の先頭と末尾に一致します。 。

行後のアサーション

JavaScript 言語の正規表現は、先読みと否定先読みのみをサポートし、後読みと否定先読みはサポートしません。 ES2018 では lookbehind が導入されており、これは V8 エンジン バージョン 4.9 (Chrome 62) ですでにサポートされています。

「先読みアサーション」とは、xy の前にのみ一致することを意味し、/x(?=y)/ として記述する必要があります。たとえば、パーセント記号の前の数値のみを照合するには、「/\d+(?=%)/」と記述します。 「否定的なアサーションを先読みする」とは、xy より前にない場合にのみ一致することを意味し、/x(?!y)/ として記述する必要があります。たとえば、パーセント記号の前にない数値のみを照合するには、「/\d+(?!%)/」と記述します。

/\d+(?=%)/.exec('米国大統領の 100% が男性') // ["100"]
/\d+(?!%)/.exec('これで 44 個すべてです') // ["44"]

上記の 2 つの文字列の間で正規表現を交換しても、同じ結果は得られません。また、「プリエンプティブアサーション」((?=%))の括弧内の部分が戻り結果に含まれていないこともわかります。

「後読みアサーション」は「先読みアサーション」の逆です。「x」は「y」の後にのみ一致し、「/(?<=y)x/」と記述する必要があります。たとえば、ドル記号の後の数字のみを照合するには、「/(?<=$)\d+/」と記述します。 「次の否定的なアサーション」は「前の否定的なアサーション」の逆です。「x」は「y」に続かない場合にのみ一致し、「/(?<!y)x/」と記述する必要があります。たとえば、ドル記号の後ろにない数値のみを照合するには、「/(?<!$)\d+/」と記述します。

/(?<=\$)\d+/.exec('100 ドル札にはベンジャミン フランクリンが載っています') // ["100"]
/(?<!\$)\d+/.exec('約 90 ユーロの価値があります') // ["90"]

上記例では、「バックラインアサーション」の括弧内の部分((?<=\$))は返される結果には含まれません。

次の例では、文字列の置換に行の後ろのアサーションを使用します。

const RE_DOLLAR_PREFIX = /(?<=\$)foo/g;
'$foo %foo foo'.replace(RE_DOLLAR_PREFIX, 'bar');
// '$bar %foo foo'

上記のコードでは、ドル記号の後の foo のみが置き換えられます。

「アサーションの後ろ」の実装は、最初に /(?<=y)x/x と一致し、次に左に戻って y 部分と一致する必要があります。この「最初に右、次に左」という実行順序は、他のすべての通常の操作とは異なり、予期しない動作を引き起こします。

まず、以下のアサーションのグループマッチングが通常の結果と異なります。

/(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]
/^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]

上記のコードでは、2 つのグループ一致をキャプチャする必要があります。 「ビハインドラインアサーション」がない場合、最初の括弧は貪欲モードであり、2 番目の括弧は 1 文字しかキャプチャできないため、結果は 1053 になります。 「バックライン アサーション」を実行する場合、実行順序は右から左であるため、2 番目の括弧は貪欲モードで、最初の括弧は 1 文字しかキャプチャできないため、結果は 1053 になります。

次に、「バックライン アサーション」のバックスラッシュ参照も通常の順序に反しており、対応する括弧の前に配置する必要があります。

/(?<=(o)d\1)r/.exec('hodor') // null
/(?<=\1d(o))r/.exec('hodor') // ["r", "o"]

上記のコードでは、次の行アサーションのバックスラッシュ参照 (\1) が括弧の後に配置されている場合、一致する結果は得られません。これは前に配置する必要があります。バックライン アサーションは最初に左から右にスキャンし、一致するものが見つかった後に戻って、右から左へのバックスラッシュ参照を完了するためです。

Unicode 属性クラス

ES2018 導入 Unicode プロパティ クラス。\p{...}\P{.. .} (\P\p の否定形です) は Unicode 文字のタイプを表し、条件を満たすすべての文字と一致します。

const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π') // true

上記のコードでは、「\p{Script=Greek}」はギリシャ文字と一致することを意味するため、「π」と一致すると成功します。

Unicode 属性クラスの標準形式では、属性名と属性値の両方を指定する必要があります。

\p{Unicodeプロパティ名=Unicodeプロパティ値}

ただし、一部の属性については、属性名のみ、または属性値のみを記述できます。

\p{Unicodeプロパティ名}
\p{Unicodeプロパティ値}

\P{…}\p{…} の逆一致、つまり、条件を満たさない文字と一致します。

これら 2 つのカテゴリは Unicode でのみ有効であるため、使用する場合は「u」修飾子を追加する必要があることに注意してください。 「u」修飾子を追加しない場合、「\p」と「\P」を使用した正規表現はエラーを報告します。

Unicode には非常に多くのプロパティがあるため、この新しいクラスは非常に表現力豊かです。

const regex = /^\p{10 進数}+$/u;
regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼') // true

上記のコードでは、属性クラスですべての小数文字の一致が指定されており、さまざまなフォントの小数文字が正常に一致することがわかります。

\p{Number} はローマ数字にも一致します。

// すべての数値と一致する
const regex = /^\p{数値}+$/u;
regex.test('²³¹⁄⁄¾') // true
regex.test('㉛㉜㉝') // true
regex.test('ⅠⅡⅢIVVVIVIⅧIXXⅪⅫ') // true

その他の例をいくつか示します。

// すべてのスペースに一致
\p{ホワイトスペース}

// 16 進数の文字に一致します
\p{16 進数}

// さまざまなテキストのすべての文字と一致します。\w の Unicode バージョンに相当します。
[\p{アルファベット}\p{マーク}\p{10 進数}\p{コネクタの句読点}\p{結合_コントロール}]

// さまざまなテキスト内のすべての文字以外の文字と一致します。Unicode バージョンの \W に相当します。
[^\p{アルファベット}\p{マーク}\p{10 進数}\p{コネクタの句読点}\p{結合_コントロール}]

// 絵文字に一致
/\p{拡張絵文字}/u

// すべての矢印文字に一致します
const regexArrows = /^\p{Block=Arrows}+$/u;
regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇔⇕⇖⇗⇘⇙⇧⇩') // true

v 修飾子: Unicode 属性クラス操作

場合によっては、Unicode 属性クラスに文字を追加または削除する必要がある場合があります。つまり、属性クラスに対して操作を実行する必要があります。 ES2024 Unicode属性クラスの操作機能を追加しました。

これは 2 つの形式の演算を提供します。1 つは差分演算 (セット A からセット B を引く)、もう 1 つは交差演算です。

// 差分演算(AマイナスB)
[A--B]

//交差演算(AとBの交差)
[A&&B]

上記の 2 つの書き方では、A と B は文字クラス ([a-z] など) または Unicode 属性クラス (\p{ASCII} など) のいずれかです。

さらに、この操作は、角括弧内に角括弧を埋め込むこと、つまり角括弧のネストをサポートします。

// ネストされた角括弧の例
[A--[0-9]]

この操作の前提条件は、正規表現で新しく導入された v 修飾子を使用する必要があることです。前述したように、Unicode 属性クラスは u 修飾子とともに使用する必要があります。この v 修飾子を使用する場合は、u を記述する必要はありません。

以下にいくつかの例を示します。

// 小数文字は ASCII コード 0 ~ 9 を削除します
[\p{10 進数}--[0-9]]

// 絵文字は ASCII コード文字を削除します
[\p{絵文字}--\p{ASCII}]

実際の例を見ると、「0」は 10 進文字クラスに属します。

/[\p{Decimal_Number}]/u.test('0') // true

上の例では、文字クラスは Unicode 固有であるため、「u」修飾子を使用する必要があります。

「0-9」が 10 進文字クラスから削除された場合、「0」はこのクラスに属しません。

/[\p{10 進数}--[0-9]]/v.test('0') // false

上記の例では、「v」修飾子は Unicode でのみ使用できるため、「u」修飾子は省略できます。

名前付きグループのマッチング

導入

正規表現では、グループの一致にかっこを使用します。

const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;

上記のコードでは、正規表現内に 3 セットのか​​っこがあります。 「exec」メソッドを使用すると、これら 3 組の一致結果を抽出できます。

const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1] // 1999 年
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

グループ マッチングに関する 1 つの問題は、各グループのマッチングの意味がわかりにくく、グループの順序が変わると数値シーケンス番号 (「matchObj[1]」など) でしか参照できないことです。参照する場合はシーケンス番号を変更する必要があります。

ES2018 では、名前付きキャプチャ グループ が導入されました。これにより、各グループの一致に名前を指定できるようになり、コードと引用符の読み取りに便利です。

const RE_DATE = /(?<年>\d{4})-(?<月>\d{2})-(?<日>\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year // "1999"
const month = matchObj.groups.month; // "12";
const day = matchObj.groups.day // "31";

上記のコードでは、「名前付きグループのマッチング」が括弧内にあり、パターンの先頭に「疑問符 + 山括弧 + グループ名」(?<年>) が追加され、その後に「結果のグループ」が追加されます。 exec メソッドで返すことができます。 属性のグループ名を参照します。同時に、数値シーケンス番号 (matchObj[1]`) は引き続き有効です。

名前付きグループのマッチングは、マッチングの目的の説明を容易にするために、マッチングの各グループに ID を追加することと同じです。グループの順序が変わってもマッチング後の処理コードを変更する必要はありません。

名前付きグループに一致するものがない場合、対応する groups オブジェクト プロパティは 未定義 になります。

const RE_OPT_A = /^(?<as>a+)?$/;
const matchObj = RE_OPT_A.exec('');

matchObj.groups.as // 未定義
matchObj.groups'as' // true

上記のコードでは、名前付きグループ as に一致するものが見つからないため、matchObj.groups.as 属性の値は unknown となり、キー名 as は常に groups に存在します。

代入と置換の構造化

名前付きグループ マッチングでは、構造化代入を使用して、マッチング結果から直接変数に値を割り当てることができます。

let {グループ: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
1 // フー
2 // バー

文字列を置換するときは、「$<グループ名>」を使用して名前付きグループを参照します。

let re = /(?<年>\d{4})-(?<月>\d{2})-(?<日>\d{2})/u;

'2015-01-02'.replace(re, '$<日>/$<月>/$<年>')
// '2015/02/01'

上記のコードでは、「replace」メソッドの 2 番目のパラメータは正規表現ではなく文字列です。

replace メソッドの 2 番目のパラメータは関数にすることもできます。この関数のパラメータのシーケンスは次のとおりです。

'2015-01-02'.replace(re, (
   matched, // 一致結果全体 2015-01-02
   Capture1, // 最初のグループは 2015 と一致します
   Capture2, // 2 番目のグループは 01 と一致します
   Capture3, // 3 番目のグループは 02 と一致します
   Position, // 開始位置 0 に一致します
   S, // 元の文字列 2015-01-02
   groups // 名前付きグループで構成されるオブジェクト {年、月、日}
 ) => {
 {日、月、年} = グループとします。
 `${日}/${月}/${年}`を返します。
});

名前付きグループのマッチングでは、元のベースに最後の関数パラメータ、つまり名前付きグループで構成されるオブジェクトが追加されます。このオブジェクトは構造を分解して関数内で直接割り当てることができます。

引用

正規表現内で「名前付きグループの一致」を参照したい場合は、\k<グループ名> を使用できます。

const RE_TWICE = /^(?<単語>[a-z]+)!\k<単語>$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false

数値参照 (\1) は引き続き機能します。

const RE_TWICE = /^(?<単語>[a-z]+)!\1$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false

両方の引用構文を同時に使用することもできます。

const RE_TWICE = /^(?<単語>[a-z]+)!\k<単語>!\1$/;
RE_TWICE.test('abc!abc!abc') // true
RE_TWICE.test('abc!abc!ab') // false

d 修飾子: 通常の一致インデックス

現在、元の文字列内のグループ マッチング結果の開始位置と終了位置を取得するのはあまり便利ではありません。通常インスタンスの「exec()」メソッドには「index」属性があり、マッチング結果全体の開始位置を取得することができます。ただし、グループマッチングにおける各グループの開始位置を取得するのは困難です。

ES2022 d 修飾子を追加しました。これにより、exec()match() が結果を返せるようになります indices 属性を追加しました、試合の開始位置と終了位置を取得できます。

const テキスト = 'zabbcdef';
const re = /ab/d;
const result = re.exec(テキスト);

result.index // 1
result.indexes // [ [1, 3] ]

上記の例では、exec() メソッドは result を返し、その index 属性は一致結果全体 (ab) の開始位置です。正規表現 re には d 修飾子があるため、result には追加の indices 属性が追加されます。このプロパティは配列であり、その各メンバーも配列であり、元の文字列内の一致結果の開始位置と終了位置が含まれます。上の例の正規表現 re にはグループ マッチングが含まれていないため、配列 indices にはメンバーが 1 つだけあり、一致全体の開始位置が 1 で終了位置が 3 であることを示します。

なお、開始位置はマッチング結果に含まれており、マッチング結果の先頭文字の位置に相当します。ただし、終了位置は照合結果には含まれず、照合結果の次の文字となります。たとえば、上記の例の照合結果の最後の文字「b」の位置は元の文字列の位置 2 であり、終了位置「3」は次の文字の位置になります。

正規表現にグループ一致が含まれる場合、「indices」属性に対応する配列には複数のメンバーが含まれ、各グループ一致の開始位置と終了位置が提供されます。

const テキスト = 'zabbcdef';
const re = /ab+(cd)/d;
const result = re.exec(テキスト);

result.indices // [ [ 1, 6 ], [ 4, 6 ] ]

上記の例では、正規表現 re にグループ一致 (cd) が含まれており、indices 属性配列には 2 つのメンバーがあり、最初のメンバーは一致結果全体 (abcd) の開始位置と終了位置です。 ) 位置の場合、2 番目のメンバーはグループ一致 (cd) の開始位置と終了位置です。

以下は複数のグループのマッチングの例です。

const テキスト = 'zabbcdef';
const re = /ab+(cd(ef))/d;
const result = re.exec(テキスト);

result.indices // [ [1, 8], [4, 8], [6, 8] ]

上の例では、正規表現 re に 2 つのグループ一致が含まれているため、indices 属性配列には 3 つのメンバーがあります。

正規表現に名前付きグループの一致が含まれている場合、indices プロパティ配列には groups プロパティも含まれます。このプロパティは、名前付きグループ一致の開始位置と終了位置を取得できるオブジェクトです。

const テキスト = 'zabbcdef';
const re = /ab+(?<Z>cd)/d;
const result = re.exec(テキスト);

result.indices.groups // { Z: [ 4, 6 ] }

上記の例では、「exec()」メソッドによって返される結果の「indices.groups」プロパティは、「Z」に一致する名前付きグループの開始位置と終了位置を提供するオブジェクトです。

グループ マッチングの取得に失敗した場合、indices プロパティ配列の対応するメンバーは unknown になり、indices.groups プロパティ オブジェクトの対応するメンバーも unknown になります。

const テキスト = 'zabbcdef';
const re = /ab+(?<Z>ce)?/d;
const result = re.exec(テキスト);

result.indices[1] // 未定義
result.indices.groups['Z'] // 未定義

上記の例では、グループ マッチング 'ce' が失敗しているため、'indices' 属性配列および 'indices.groups' 属性オブジェクトに対応するグループ マッチング メンバー 'Z' は両方とも '未定義' です。

String.prototype.matchAll()

正規表現の文字列内に複数の一致がある場合は、「g」修飾子または「y」修飾子を使用して、ループ内でそれらを 1 つずつ削除するのが一般的です。

var regex = /t(e)(st(\d?))/g;
var string = 'test1test2test3';

var は = [] に一致します。
var の一致。
while (match = regex.exec(string)) {
  マッチ.プッシュ(マッチ);
}

マッチ
//[
// ["test1", "e", "st1", "1", インデックス: 0, 入力: "test1test2test3"],
// ["test2", "e", "st2", "2", インデックス: 5, 入力: "test1test2test3"],
// ["test3", "e", "st3", "3", インデックス: 10, 入力: "test1test2test3"]
//]

上記のコードでは、while ループが各ラウンド (合計 3 ラウンド) で通常のマッチングを取り出します。

ES2020 すべての一致を一度に取得できる String.prototype.matchAll() メソッドを追加しました。ただし、配列ではなく反復子を返します。

const 文字列 = 'テスト1テスト2テスト3';
const regex = /t(e)(st(\d?))/g;

for (string.matchAll(regex) の定数一致) {
  console.log(一致);
}
// ["test1", "e", "st1", "1", インデックス: 0, 入力: "test1test2test3"]
// ["test2", "e", "st2", "2", インデックス: 5, 入力: "test1test2test3"]
// ["test3", "e", "st3", "3", インデックス: 10, 入力: "test1test2test3"]

上記のコードでは、string.matchAll(regex) がトラバーサーを返すので、for...of ループを使用して取り出すことができます。配列を返す場合と比較して、トラバーサーを返す利点は、一致する結果が大きな配列の場合に、トラバーサーがリソースを節約できることです。

イテレータから配列への変換は非常に簡単で、... 演算子と Array.from() メソッドを使用するだけです。

// 配列に変換する方法1
[...string.matchAll(正規表現)]

// 配列への変換方法2
Array.from(string.matchAll(regex))

作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0