RegExp)/uによるUnicodeモードregExp.test(str):マッチがありますか? [ES3]str.search(regExp):マッチはどのインデックスにありますか? [ES3]regExp.exec(str):キャプチャグループ [ES3]str.match(regExp):すべてのグループ0キャプチャを取得する [ES3]str.matchAll(regExp):すべてのマッチオブジェクトに対するイテラブルを取得する [ES2020]regExp.exec() vs. str.match() vs. str.matchAll()str.replace()とstr.replaceAll()による置換/gと/y、およびプロパティ.lastIndex(上級)/gと/y/gと/yの影響/gと/yの4つの落とし穴とその対処法.lastIndexのユースケース:指定したインデックスからのマッチング開始.lastIndexの欠点.global(/g)と.sticky(/y) 機能の可用性
特に明記されていない限り、すべての正規表現機能はES3以降で使用可能です。
正規表現を作成する主な方法は2つあります。
リテラル:静的にコンパイルされます(ロード時)。
/abc/uiコンストラクタ:動的にコンパイルされます(実行時)。
new RegExp('abc', 'ui')どちらの正規表現も、次の2つの部分を持ちます。
abc – 実際の正規表現。uとi。フラグは、パターンがどのように解釈されるかを設定します。たとえば、iは大文字と小文字を区別しないマッチングを有効にします。使用可能なフラグのリストは、本章の後半で説明します。コンストラクタRegExp()には2つのバリエーションがあります。
new RegExp(pattern : string, flags = '') [ES3]
patternで指定されたとおりに新しい正規表現が作成されます。flagsが省略されている場合、空文字列''が使用されます。
new RegExp(regExp : RegExp, flags = regExp.flags) [ES6]
regExpが複製されます。flagsが指定されている場合、それはクローンフラグを決定します。
2番目のバリエーションは、正規表現を複製する際に、オプションで変更する場合に役立ちます。フラグは不変であり、これはフラグを変更する唯一の方法です。例:
function copyAndAddFlags(regExp, flagsToAdd='') {
// The constructor doesn’t allow duplicate flags;
// make sure there aren’t any:
const newFlags = Array.from(
new Set(regExp.flags + flagsToAdd)
).join('');
return new RegExp(regExp, newFlags);
}
assert.equal(/abc/i.flags, 'i');
assert.equal(copyAndAddFlags(/abc/i, 'g').flags, 'gi');正規表現の最上位レベルでは、次の構文文字は特殊です。これらは、バックスラッシュ(\)をプレフィックスとして付けることでエスケープされます。
\ ^ $ . * + ? ( ) [ ] { } |
正規表現リテラルでは、スラッシュをエスケープする必要があります。
> /\//.test('/')
truenew RegExp()の引数では、スラッシュをエスケープする必要はありません。
> new RegExp('/').test('/')
trueアトムは、正規表現の基本的な構成要素です。
^、$など)を除くすべての文字です。パターン文字はそれ自身に一致します。例:A b %.は任意の文字に一致します。フラグ/s(dotAll)を使用して、ドットが改行文字に一致するかどうかを制御できます(詳細は後述)。\f:フォームフィード(FF)\n:ラインフィード(LF)\r:キャリッジリターン(CR)\t:水平タブ\v:垂直タブ\cA(Ctrl-A)、…、\cZ(Ctrl-Z)\u00E4/uが必要):\u{1F44D}\d:数字([0-9]と同じ)\D:数字以外\w:単語文字([A-Za-z0-9_]と同じ。プログラミング言語の識別子に関連)\W:単語文字以外\s:空白文字(スペース、タブ、改行文字など)\S:空白文字以外\p{White_Space}、\P{White_Space}など。/uが必要です。Unicode標準では、各文字にプロパティ(メタデータ)があります。プロパティは、文字の性質を定義する上で重要な役割を果たします。Unicode標準、3.3節、D3を引用すると、
文字の意味論は、その同一性、規範的なプロパティ、および動作によって決定されます。
いくつかのプロパティの例を以下に示します。
Name:一意の名前。大文字、数字、ハイフン、スペースで構成されます。例:Name = LATIN CAPITAL LETTER A🙂: Name = SLIGHTLY SMILING FACEGeneral_Category:文字を分類します。例:General_Category = Lowercase_LetterGeneral_Category = Currency_SymbolWhite_Space:スペース、タブ、改行文字などの見えないスペース文字をマークするために使用されます。例:White_Space = TrueWhite_Space = FalseAge:文字が導入されたUnicode標準のバージョン。例:ユーロ記号€は、Unicode標準のバージョン2.1に追加されました。Age = 2.1Block:連続したコードポイントの範囲。ブロックは重複せず、名前は一意です。例:Block = Basic_Latin(範囲U+0000..U+007F)🙂: Block = Emoticons(範囲U+1F600..U+1F64F)Script:1つ以上の筆記体系で使用される文字の集合です。Script = GreekScript = CyrillicUnicodeプロパティエスケープは次のようになります。
\p{prop=value}:プロパティpropの値がvalueであるすべての文字に一致します。\P{prop=value}:プロパティpropの値がvalueでないすべての文字に一致します。\p{bin_prop}:バイナリプロパティbin_propがTrueであるすべての文字に一致します。\P{bin_prop}:バイナリプロパティbin_propがFalseであるすべての文字に一致します。コメント
Unicodeプロパティエスケープは、フラグ/uが設定されている場合にのみ使用できます。/uなしでは、\pはpと同じです。
形式(3)と(4)は、プロパティがGeneral_Categoryの場合、省略形として使用できます。たとえば、次の2つのエスケープは同等です。
\p{Uppercase_Letter}
\p{General_Category=Uppercase_Letter}例
空白文字の確認
> /^\p{White_Space}+$/u.test('\t \n\r')
trueギリシャ文字の確認
> /^\p{Script=Greek}+$/u.test('μετά')
true任意の文字の削除
> '1π2ü3é4'.replace(/\p{Letter}/ug, '')
'1234'小文字の削除
> 'AbCdEf'.replace(/\p{Lowercase_Letter}/ug, '')
'ACE'参考資料
文字クラスは、角括弧内にクラス範囲を囲みます。クラス範囲は文字の集合を指定します。
[«class ranges»]は、集合内の任意の文字に一致します。[^«class ranges»]は、集合内にない任意の文字に一致します。クラス範囲のルール
構文文字以外の文字はそれ自体を表します:[abc]
次の4つの文字だけが特殊であり、スラッシュでエスケープする必要があります。
^ \ - ]
^は、先頭に来る場合にのみエスケープする必要があります。-は、先頭または末尾に来る場合はエスケープする必要がありません。文字エスケープ(\n、\u{1F44D}など)は、通常の使用方法と同じです。
\bはバックスペースを表します。正規表現の他の場所では、単語境界に一致します。文字クラスエスケープ(\d、\p{White_Space}など)は、通常の使用方法と同じです。
文字の範囲はハイフンで指定します: [a-z]
(#+)\1、\2など(?<hashes>#+)\k<hashes>(?:#+)デフォルトでは、以下のすべての量指定子は貪欲です(可能な限り多くの文字にマッチします)
?: 0回または1回マッチ*: 0回以上マッチ+: 1回以上マッチ{n}: n回マッチ{n,}: n回以上マッチ{n,m}: 少なくともn回、最大m回マッチ控えめ(可能な限り少ない文字にマッチ)にするには、後に疑問符(?)を付けます
> /".*"/.exec('"abc"def"')[0] // greedy
'"abc"def"'
> /".*?"/.exec('"abc"def"')[0] // reluctant
'"abc"'^は入力の先頭でのみマッチします$は入力の末尾でのみマッチします\bは単語境界でのみマッチします\Bは単語境界でない場合にのみマッチします肯定先読み: (?=«パターン»)は、パターンが次に来るものにマッチする場合にマッチします。
例:小文字の文字列で、その後にXが続くもの。
> 'abcX def'.match(/[a-z]+(?=X)/g)
[ 'abc' ]X自体はマッチした部分文字列の一部ではありません。
否定先読み: (?!«パターン»)は、パターンが次に来るものにマッチしない場合にマッチします。
例:小文字の文字列で、その後にXが続かないもの。
> 'abcX def'.match(/[a-z]+(?!X)/g)
[ 'ab', 'def' ]肯定後読み: (?<=«パターン»)は、パターンが前に来たものにマッチする場合にマッチします。
例:小文字の文字列で、その前にXが続くもの。
> 'Xabc def'.match(/(?<=X)[a-z]+/g)
[ 'abc' ]否定後読み: (?<!«パターン»)は、パターンが前に来たものにマッチしない場合にマッチします。
例:小文字の文字列で、その前にXが続かないもの。
> 'Xabc def'.match(/(?<!X)[a-z]+/g)
[ 'bc', 'def' ]例:「.js」を「.html」に置き換えますが、「Node.js」の場合は置き換えません。
> 'Node.js: index.js and main.js'.replace(/(?<!Node)\.js/g, '.html')
'Node.js: index.html and main.html'|)注意点: この演算子の優先順位は低いです。必要に応じてグループを使用してください
^aa|zz$は、aaで始まり、かつ/またはzzで終わるすべての文字列にマッチします。|の優先順位は^と$よりも低いことに注意してください。^(aa|zz)$は、文字列'aa'と'zz'にマッチします。^a(a|z)z$は、文字列'aaz'と'azz'にマッチします。| リテラルフラグ | プロパティ名 | ES | 説明 |
|---|---|---|---|
d |
hasIndices |
ES2022 | マッチインデックスを有効にする |
g |
global |
ES3 | 複数回マッチする |
i |
ignoreCase |
ES3 | 大文字小文字を区別せずにマッチする |
m |
multiline |
ES3 | ^と$が行単位でマッチする |
s |
dotAll |
ES2018 | ドットが改行文字にマッチする |
u |
unicode |
ES6 | Unicodeモード(推奨) |
y |
sticky |
ES6 | マッチ間の文字がない |
JavaScriptでは以下の正規表現フラグが使用できます(表 21 は簡潔な概要を示しています)
/d (.hasIndices): 一部のRegExp関連メソッドは、正規表現が入力文字列のどこでマッチしたかを記述するマッチオブジェクトを返します。このフラグがオンの場合、各マッチオブジェクトには、各グループキャプチャの開始位置と終了位置を示すマッチインデックスが含まれます。詳細情報: §43.5.1 「マッチオブジェクトにおけるマッチインデックス [ES2022]」.
/g (.global) は、以下のメソッドの動作を根本的に変更します。
RegExp.prototype.test()RegExp.prototype.exec()String.prototype.match()その方法は、§43.7 「フラグ/gと/y、およびプロパティ.lastIndex」で説明されています。簡単に言うと、/gがない場合、これらのメソッドは入力文字列における正規表現の最初のマッチのみを考慮します。/gを使用すると、すべてのマッチを考慮します。
/i (.ignoreCase) は、大文字小文字を区別しないマッチを有効にします
> /a/.test('A')
false
> /a/i.test('A')
true/m (.multiline): このフラグがオンの場合、^は各行の先頭にマッチし、$は各行の末尾にマッチします。オフの場合、^は入力文字列全体の先頭にマッチし、$は入力文字列全体の末尾にマッチします。
> 'a1\na2\na3'.match(/^a./gm)
[ 'a1', 'a2', 'a3' ]
> 'a1\na2\na3'.match(/^a./g)
[ 'a1' ]/u (.unicode): このフラグは、正規表現のUnicodeモードを有効にします。そのモードについては、次の小節で説明します。
/y (.sticky): このフラグは、主に/gと組み合わせて意味を持ちます。両方がオンの場合、任意のマッチは前のマッチに直接続く必要があります(つまり、正規表現オブジェクトのインデックス.lastIndexで始まる必要があります)。したがって、最初のマッチはインデックス0でなければなりません。
> 'a1a2 a3'.match(/a./gy)
[ 'a1', 'a2' ]
> '_a1a2 a3'.match(/a./gy) // first match must be at index 0
null
> 'a1a2 a3'.match(/a./g)
[ 'a1', 'a2', 'a3' ]
> '_a1a2 a3'.match(/a./g)
[ 'a1', 'a2', 'a3' ]/yの主な使用例はトークン化(構文解析中)です。このフラグの詳細については、§43.7 「フラグ/gと/y、およびプロパティ.lastIndex」を参照してください。
/s (.dotAll): デフォルトでは、ドットは改行文字にマッチしません。このフラグを使用すると、マッチします
> /./.test('\n')
false
> /./s.test('\n')
true回避策: /sがサポートされていない場合は、ドットの代わりに[^]を使用できます。
> /[^]/.test('\n')
true次の正規表現を考えてみます: /“([^”]+)”/udg
フラグはどのような順序でリストする必要がありますか?2つの選択肢があります
/dgu/uが最も基本的ななど):/ugd(2)は自明ではないため、(1)の方が良い選択肢です。JavaScriptもRegExpプロパティ.flagsでこれを使用しています
> /a/ismudgy.flags
'dgimsuy'/uによるUnicodeモードフラグ/uは、正規表現の特別なUnicodeモードを有効にします。そのモードでは、いくつかの機能が有効になります
パターンでは、\u{1F42A}などのUnicodeコードポイントエスケープを使用して文字を指定できます。\u03B1などのコードユニットエスケープは、16進数の4桁の範囲しかありません(基本多言語面に対応)。
パターンでは、\p{White_Space}などのUnicodeプロパティエスケープを使用できます。
多くのエスケープは禁止されています。例: \a \- \:
パターン文字は常にそれ自身にマッチします
> /pa-:/.test('pa-:')
true/uがない場合、バックスラッシュでエスケープした場合でも、それ自身にマッチするパターン文字がいくつかあります
> /\p\a\-\:/.test('pa-:')
true/uを使用する場合
\pはUnicodeプロパティエスケープを開始します。マッチングの原子単位は、JavaScript文字(コードユニット)ではなく、Unicode文字(コードポイント)です。
次の小節では、最後の項目を詳しく説明します。原子単位がUnicode文字である場合とJavaScript文字である場合を説明するために、次のUnicode文字を使用します
const codePoint = '🙂';
const codeUnits = '\uD83D\uDE42'; // UTF-16
assert.equal(codePoint, codeUnits); // same string!JavaScriptがどのように処理するかを示すために、🙂と\uD83D\uDE42を切り替えるだけです。どちらも同等であり、文字列と正規表現で交換して使用できます。
/uを使用すると、🙂の2つのコードユニットは1つの文字として扱われます
> /^[🙂]$/u.test('🙂')
true/uを使用しないと、🙂は2つの文字として扱われます
> /^[\uD83D\uDE42]$/.test('\uD83D\uDE42')
false
> /^[\uD83D\uDE42]$/.test('\uDE42')
true^と$は、入力文字列が1文字であることを要求するため、最初の結果はfalseになります。
.)は、JavaScript文字ではなく、Unicode文字にマッチします/uを使用すると、ドット演算子はUnicode文字にマッチします
> '🙂'.match(/./gu).length
1.match()と/gは、正規表現のすべてのマッチを含む配列を返します。
/uを使用しないと、ドット演算子はJavaScript文字にマッチします
> '\uD83D\uDE80'.match(/./g).length
2/uを使用すると、量指定子は先行するUnicode文字全体に適用されます
> /^🙂{3}$/u.test('🙂🙂🙂')
true/uを使用しないと、量指定子は先行するJavaScript文字のみに適用されます
> /^\uD83D\uDE80{3}$/.test('\uD83D\uDE80\uDE80\uDE80')
true注目すべき点
.lastIndexだけが実際の実例プロパティです。他のすべてプロパティはゲッターを介して実装されます。.lastIndexは唯一の変更可能なプロパティです。他のすべてのプロパティは読み取り専用です。変更したい場合は、正規表現をコピーする必要があります(詳細については、§43.1.2 「正規表現の複製と非破壊的な変更」を参照してください)。各正規表現フラグは、より長く、より記述的な名前を持つプロパティとして存在します
> /a/i.ignoreCase
true
> /a/.ignoreCase
falseこれはフラグプロパティの完全なリストです
.dotAll (/s).global (/g).hasIndices (/d).ignoreCase (/i).multiline (/m).sticky (/y).unicode (/u)各正規表現には、次のプロパティもあります
.source [ES3]: 正規表現パターン
> /abc/ig.source
'abc'.flags [ES6]: 正規表現のフラグ
> /abc/ig.flags
'gi'.lastIndex [ES3]: フラグ/gがオンの場合に使用されます。§43.7 「フラグ/gと/y、およびプロパティ.lastIndex」で詳細を参照してください。
いくつかの正規表現関連メソッドは、正規表現が入力文字列のどこでマッチしたかについての詳細情報を提供するために、いわゆるマッチオブジェクトを返します。これらのメソッドは次のとおりです。
RegExp.prototype.exec()はnullまたは単一のマッチオブジェクトを返します。String.prototype.match()はnullまたは単一のマッチオブジェクトを返します(フラグ/gが設定されていない場合)。String.prototype.matchAll()はマッチオブジェクトのイテラブルを返します(フラグ/gを設定する必要があります。そうでない場合、例外がスローされます)。これは例です
assert.deepEqual(
/(a+)b/d.exec('ab aaab'),
{
0: 'ab',
1: 'a',
index: 0,
input: 'ab aaab',
groups: undefined,
indices: {
0: [0, 2],
1: [0, 1],
groups: undefined
},
}
);.exec()の結果は、次のプロパティを持つ最初のマッチのマッチオブジェクトです
[0]: 正規表現によってマッチした完全な部分文字列[1]: 番号付きグループ1のキャプチャ(など).index: マッチはどこで発生しましたか?.input: マッチされた文字列.groups: 名前付きグループのキャプチャ(§43.6.4.2 「名前付きキャプチャグループ [ES2018]」を参照).indices: キャプチャされたグループのインデックス範囲/dがオンの場合にのみ作成されます。マッチインデックスはマッチオブジェクトの機能です。正規表現フラグ/d(プロパティ.hasIndices)をオンにすると、グループがキャプチャされた開始インデックスと終了インデックスが記録されます。
番号付きグループのキャプチャへのアクセス方法を示します
const matchObj = /(a+)(b+)/d.exec('aaaabb');
assert.equal(
matchObj[1], 'aaaa'
);
assert.equal(
matchObj[2], 'bb'
);正規表現フラグ/dにより、matchObjには、入力文字列で番号付きグループがキャプチャされた場所を記録するプロパティ.indicesもあります
assert.deepEqual(
matchObj.indices[1], [0, 4]
);
assert.deepEqual(
matchObj.indices[2], [4, 6]
);名前付きグループのキャプチャは、このようにアクセスされます
const matchObj = /(?<as>a+)(?<bs>b+)/d.exec('aaaabb');
assert.equal(
matchObj.groups.as, 'aaaa');
assert.equal(
matchObj.groups.bs, 'bb');それらのインデックスはmatchObj.indices.groupsに格納されます
assert.deepEqual(
matchObj.indices.groups.as, [0, 4]);
assert.deepEqual(
matchObj.indices.groups.bs, [4, 6]);マッチインデックスの重要なユースケースの1つは、構文エラーが正確にどこにあるのかを示すパーサーです。次のコードは関連する問題を解決します。引用されたコンテンツがどこで始まり、どこで終わるのかを示します(デモは最後を参照)。
const reQuoted = /“([^”]+)”/dgu;
function pointToQuotedText(str) {
const startIndices = new Set();
const endIndices = new Set();
for (const match of str.matchAll(reQuoted)) {
const [start, end] = match.indices[1];
startIndices.add(start);
endIndices.add(end);
}
let result = '';
for (let index=0; index < str.length; index++) {
if (startIndices.has(index)) {
result += '[';
} else if (endIndices.has(index+1)) {
result += ']';
} else {
result += ' ';
}
}
return result;
}
assert.equal(
pointToQuotedText(
'They said “hello” and “goodbye”.'),
' [ ] [ ] '
);デフォルトでは、正規表現は文字列内の任意の場所にマッチします
> /a/.test('__a__')
trueこれは、^などのアサーションを使用するか、/yフラグを使用することで変更できます。
> /^a/.test('__a__')
false
> /^a/.test('a__')
trueregExp.test(str):マッチがありますか?[ES3]正規表現メソッド.test()は、regExpがstrにマッチする場合はtrueを返します。
> /bc/.test('ABCD')
false
> /bc/i.test('ABCD')
true
> /\.mjs$/.test('main.mjs')
true.test()では、通常/gフラグを避けるべきです。使用した場合、メソッドを呼び出すたびに同じ結果が得られるとは限りません。
> const r = /a/g;
> r.test('aab')
true
> r.test('aab')
true
> r.test('aab')
falseこの結果は、/a/が文字列内に2つのマッチを持つことによるものです。それらすべてが見つかった後、.test()はfalseを返します。
str.search(regExp):マッチはどのインデックスにありますか?[ES3]文字列メソッド.search()は、regExpにマッチするstrの最初のインデックスを返します。
> '_abc_'.search(/abc/)
1
> 'main.mjs'.search(/\.mjs$/)
4regExp.exec(str):キャプチャグループ[ES3]/gフラグがない場合、.exec()はstr内のregExpの最初のマッチに対するマッチオブジェクトを返します。
assert.deepEqual(
/(a+)b/.exec('ab aab'),
{
0: 'ab',
1: 'a',
index: 0,
input: 'ab aab',
groups: undefined,
}
);前の例には、番号付きグループが1つ含まれていました。次の例は、名前付きグループを示しています。
assert.deepEqual(
/(?<as>a+)b/.exec('ab aab'),
{
0: 'ab',
1: 'a',
index: 0,
input: 'ab aab',
groups: { as: 'a' },
}
);.exec()の結果からわかるように、名前付きグループは番号付きグループでもあります。そのキャプチャは2回存在します。
'1')として。groups.as)として。 すべてのマッチを取得するためのより良い代替手段:
str.matchAll(regExp) [ES2020]
ECMAScript 2020以降、JavaScriptにはすべてのマッチを取得するための別のメソッドがあります。str.matchAll(regExp)。このメソッドは使いやすく、注意点も少なくなっています。
正規表現のすべてのマッチ(最初の1つだけではない)を取得する場合は、/gフラグをオンにする必要があります。その後、.exec()を複数回呼び出して、毎回1つのマッチを取得できます。最後のマッチの後、.exec()はnullを返します。
> const regExp = /(a+)b/g;
> regExp.exec('ab aab')
{ 0: 'ab', 1: 'a', index: 0, input: 'ab aab', groups: undefined }
> regExp.exec('ab aab')
{ 0: 'aab', 1: 'aa', index: 3, input: 'ab aab', groups: undefined }
> regExp.exec('ab aab')
nullしたがって、すべてのマッチを次のようにループ処理できます。
const regExp = /(a+)b/g;
const str = 'ab aab';
let match;
// Check for null via truthiness
// Alternative: while ((match = regExp.exec(str)) !== null)
while (match = regExp.exec(str)) {
console.log(match[1]);
}
// Output:
// 'a'
// 'aa'
/g付きの正規表現を共有する際には注意してください!
/g付きの正規表現を共有することにはいくつかの落とし穴があり、それについては後で説明します。
練習問題:
.exec()を使用した引用符付きテキストの抽出
exercises/regexps/extract_quoted_test.mjs
str.match(regExp):すべてのグループ0キャプチャを取得する[ES3]/gがない場合、.match()は.exec()のように動作し、単一のマッチオブジェクトを返します。
/gがある場合、.match()はregExpにマッチするstrのすべての部分文字列を返します。
> 'ab aab'.match(/(a+)b/g)
[ 'ab', 'aab' ]マッチがない場合、.match()はnullを返します。
> 'xyz'.match(/(a+)b/g)
nullnull合体演算子(??)を使用して、nullから身を守ることができます。
const numberOfMatches = (str.match(regExp) ?? []).length;str.matchAll(regExp):すべてのマッチオブジェクトに対する反復可能オブジェクトを取得する[ES2020].matchAll()は次のように呼び出されます。
const matchIterable = str.matchAll(regExp);
文字列と正規表現が与えられると、.matchAll()はすべてのマッチのマッチオブジェクトに対する反復可能オブジェクトを返します。
次の例では、Array.from()を使用して反復可能オブジェクトを配列に変換し、より適切に比較できるようにしています。
> Array.from('-a-a-a'.matchAll(/-(a)/ug))
[
{ 0:'-a', 1:'a', index: 0, input: '-a-a-a', groups: undefined },
{ 0:'-a', 1:'a', index: 2, input: '-a-a-a', groups: undefined },
{ 0:'-a', 1:'a', index: 4, input: '-a-a-a', groups: undefined },
]/gフラグを設定する必要があります。
> Array.from('-a-a-a'.matchAll(/-(a)/u))
TypeError: String.prototype.matchAll called with a non-global
RegExp argument.matchAll()はregExp.lastIndexの影響を受けず、変更もしません。
.matchAll()の実装.matchAll()は次のように.exec()を使用して実装できます。
function* matchAll(str, regExp) {
if (!regExp.global) {
throw new TypeError('Flag /g must be set!');
}
const localCopy = new RegExp(regExp, regExp.flags);
let match;
while (match = localCopy.exec(str)) {
yield match;
}
}ローカルコピーを作成することで、2つのことが保証されます。
regex.lastIndexは変更されません。localCopy.lastIndexは0になります。matchAll()の使用
const str = '"fee" "fi" "fo" "fum"';
const regex = /"([^"]*)"/g;
for (const match of matchAll(str, regex)) {
console.log(match[1]);
}
// Output:
// 'fee'
// 'fi'
// 'fo'
// 'fum'regExp.exec() 対 str.match() 対 str.matchAll()次の表は、3つのメソッドの違いをまとめたものです。
/gなし |
/gあり |
|
|---|---|---|
regExp.exec(str) |
最初のマッチオブジェクト | 次のマッチオブジェクトまたはnull |
str.match(regExp) |
最初のマッチオブジェクト | グループ0キャプチャの配列 |
str.matchAll(regExp) |
TypeError |
マッチオブジェクトに対する反復可能オブジェクト |
str.replace()とstr.replaceAll()による置換両方の置換メソッドには、2つのパラメーターがあります。
str.replace(searchValue, replacementValue)str.replaceAll(searchValue, replacementValue)searchValueは次のいずれかです。
replacementValueは次のいずれかです。
$には特別な意味があり、グループのキャプチャなどを挿入できます(詳細は後で説明します)。2つのメソッドの違いは次のとおりです。
.replace()は、/gのない文字列または正規表現の最初の出現箇所を置換します。.replaceAll()は、/gのある文字列または正規表現のすべての出現箇所を置換します。この表は、その動作をまとめたものです。
検索対象:→ |
文字列 | /gなしの正規表現 |
/gのある正規表現 |
|---|---|---|---|
.replace |
最初の出現箇所 | 最初の出現箇所 | (すべての出現箇所) |
.replaceAll |
すべての出現箇所 | TypeError |
すべての出現箇所 |
.replace()の最後の列は括弧で囲まれています。これは、このメソッドが.replaceAll()よりもずっと前に存在し、現在は後者のメソッドで処理する必要がある機能をサポートしているためです。これを変更できれば、.replace()はここでTypeErrorをスローします。
まず、replacementValueが単純な文字列(文字$なし)の場合、.replace()と.replaceAll()が個別にどのように動作するかを調べます。次に、より複雑な置換値によって両方がどのように影響を受けるかを調べます。
str.replace(searchValue, replacementValue)[ES3].replace()の動作は、その最初のパラメーターsearchValueの影響を受けます。
/gのない正規表現:この正規表現の最初のマッチを置換します。
> 'aaa'.replace(/a/, 'x')
'xaa'文字列:この文字列の最初の出現箇所を置換します(文字列は正規表現としてではなく、そのまま解釈されます)。
> 'aaa'.replace('a', 'x')
'xaa'/gのある正規表現:この正規表現のすべてのマッチを置換します。
> 'aaa'.replace(/a/g, 'x')
'xxx'推奨事項:.replaceAll()が利用可能な場合は、このメソッドを使用することをお勧めします。その目的は、複数の出現箇所を置換することです。
文字列のすべての出現箇所を置換する場合は、2つの選択肢があります。
.replaceAll()(ES2021で導入)を使用できます。
この章の後半では、[ツール関数escapeForRegExp()]が登場します。これは、文字列をその文字列に複数回マッチする正規表現に変換するのに役立ちます(例:'*'は/\*/gになります)。
str.replaceAll(searchValue, replacementValue)[ES2021].replaceAll()の動作は、その最初のパラメーターsearchValueの影響を受けます。
/gのある正規表現:この正規表現のすべてのマッチを置換します。
> 'aaa'.replaceAll(/a/g, 'x')
'xxx'文字列:この文字列のすべての出現箇所を置換します(文字列は正規表現としてではなく、そのまま解釈されます)。
> 'aaa'.replaceAll('a', 'x')
'xxx'/gのない正規表現:TypeErrorがスローされます(.replaceAll()の目的は、複数の出現箇所を置換することであるため)。
> 'aaa'.replaceAll(/a/, 'x')
TypeError: String.prototype.replaceAll called with
a non-global RegExp argument.replace()と.replaceAll()のパラメーターreplacementValueこれまで、パラメーターreplacementValueは単純な文字列でのみ使用してきましたが、もっと多くのことができます。その値が
文字列の場合、マッチはこの文字列で置換されます。文字$には特別な意味があり、グループのキャプチャなどを挿入できます(詳細については読み進めてください)。
関数の場合、マッチはこの関数によって計算された文字列で置換されます。
replacementValueは文字列です置換値が文字列の場合、ドル記号には特別な意味があります。正規表現によってマッチしたテキストを挿入します。
| テキスト | 結果 |
|---|---|
$$ |
単一の$ |
$& |
完全一致 |
$` |
マッチ前のテキスト |
$' |
マッチ後のテキスト |
$n |
番号付きグループnのキャプチャ(n > 0) |
$<name> |
名前付きグループnameのキャプチャ[ES2018] |
例:マッチした部分文字列の前、中、後にテキストを挿入します。
> 'a1 a2'.replaceAll(/a/g, "($`|$&|$')")
'(|a|1 a2)1 (a1 |a|2)2'例:番号付きグループのキャプチャを挿入します。
> const regExp = /^([A-Za-z]+): (.*)$/ug;
> 'first: Jane'.replaceAll(regExp, 'KEY: $1, VALUE: $2')
'KEY: first, VALUE: Jane'例:名前付きグループのキャプチャを挿入します。
> const regExp = /^(?<key>[A-Za-z]+): (?<value>.*)$/ug;
> 'first: Jane'.replaceAll(regExp, 'KEY: $<key>, VALUE: $<value>')
'KEY: first, VALUE: Jane' 練習問題:
.replace()と名前付きグループを使用した引用符の変更
exercises/regexps/change_quotes_test.mjs
replacementValueは関数です置換値が関数の場合、各置換を計算できます。次の例では、見つかった非負の整数を2倍にします。
assert.equal(
'3 cats and 4 dogs'.replaceAll(/[0-9]+/g, (all) => 2 * Number(all)),
'6 cats and 8 dogs'
);置換関数は、次のパラメーターを取得します。マッチオブジェクトとよく似ていることに注意してください。これらのパラメーターはすべて位置指定ですが、名前を付ける方法も示しました。
all:完全一致g1:番号付きグループ1のキャプチャindex:マッチはどこで発生しましたか?input:置換を行う文字列groups[ES2018]:名前付きグループのキャプチャ(オブジェクト)。常に最後のパラメーターです。groupsだけに関心がある場合は、次のテクニックを使用できます。
const result = 'first=jane, last=doe'.replace(
/(?<key>[a-z]+)=(?<value>[a-z]+)/g,
(...args) => { // (A)
const groups = args.at(-1); // (B)
const {key, value} = groups;
return key.toUpperCase() + '=' + value.toUpperCase();
});
assert.equal(result, 'FIRST=JANE, LAST=DOE');A行のrestパラメーターにより、argsにはすべてのパラメーターを含む配列が含まれています。B行の配列メソッド.at()を使用して、最後のパラメーターにアクセスします。
String.prototype.split()については、文字列に関する章で説明されています。String.prototype.split()の最初のパラメーターは、文字列または正規表現のいずれかです。後者の場合、グループのキャプチャが結果に表示されます。
> 'a:b : c'.split(':')
[ 'a', 'b ', ' c' ]
> 'a:b : c'.split(/ *: */)
[ 'a', 'b', 'c' ]
> 'a:b : c'.split(/( *):( *)/)
[ 'a', '', '', 'b', ' ', ' ', 'c' ]/gと/yフラグ、および.lastIndexプロパティ(高度な内容)このセクションでは、RegExpフラグ/gと/yがどのように機能し、RegExpプロパティ.lastIndexにどのように依存するかを調べます。また、驚くかもしれない.lastIndexの興味深いユースケースについても発見します。
/gと/yフラグすべてのメソッドは/gと/yに異なる反応を示します。これは、大まかな概要を示しています。
/g(.global、ES3):正規表現は、文字列内の任意の場所に複数回マッチする必要があります。/y(.sticky、ES6):文字列内の任意のマッチは、前のマッチの直後に続く必要があります(マッチは「くっついて」います)。正規表現に/gフラグも/yフラグもない場合、マッチングは1回発生し、先頭から始まります。
/gまたは/yのいずれかがある場合、マッチングは入力文字列内の「現在の位置」を基準にして実行されます。その位置は、正規表現プロパティ.lastIndexに格納されます。
正規表現関連のメソッドには3つのグループがあります。
文字列メソッド.search(regExp)と.split(regExp)は、/gと/y(したがって.lastIndexも)を完全に無視します。
RegExpメソッド.exec(str)と.test(str)は、/gまたは/yのいずれかが設定されている場合、2つの点で変化します。
まず、メソッドを繰り返し呼び出すことで、複数のマッチを取得します。毎回、別の結果(マッチオブジェクトまたはtrue)または「結果の終わり」値(nullまたはfalse)を返します。
次に、正規表現プロパティ.lastIndexを使用して入力文字列をステップ実行します。一方では、.lastIndexはマッチングが開始される場所を決定します。
/gは、マッチは.lastIndex以降で始まる必要があることを意味します。
/yは、マッチは.lastIndexで始まる必要があることを意味します。つまり、正規表現の先頭は.lastIndexに固定されます。
^と$は、通常どおりに動作し続けます。.multilineが設定されていない限り、入力文字列の先頭または末尾にマッチを固定します。設定されている場合、行の先頭または末尾に固定します。
一方、.lastIndexは、前のマッチの最後のインデックスに1を加えた値に設定されます。
その他のすべてのメソッドへの影響は以下のとおりです。
/g は複数のマッチにつながります。/y は、.lastIndexから始まる1つのマッチにつながります。/yg は、ギャップのない複数のマッチにつながります。これは概要でした。以降のセクションでは、より詳細な説明を行います。
/gと/yによってメソッドがどのように影響を受けるかregExp.exec(str) [ES3]/gと/yがない場合、.exec()は.lastIndexを無視し、常に最初のマッチに対するマッチオブジェクトを返します。
> const re = /#/; re.lastIndex = 1;
> [re.exec('##-#'), re.lastIndex]
[{ 0: '#', index: 0, input: '##-#' }, 1]
> [re.exec('##-#'), re.lastIndex]
[{ 0: '#', index: 0, input: '##-#' }, 1]/gがある場合、マッチは.lastIndex以降から始まる必要があります。.lastIndexは更新されます。マッチがない場合はnullが返されます。
> const re = /#/g; re.lastIndex = 1;
> [re.exec('##-#'), re.lastIndex]
[{ 0: '#', index: 1, input: '##-#' }, 2]
> [re.exec('##-#'), re.lastIndex]
[{ 0: '#', index: 3, input: '##-#' }, 4]
> [re.exec('##-#'), re.lastIndex]
[null, 0]/yがある場合、マッチは正確に.lastIndexから始まる必要があります。.lastIndexは更新されます。マッチがない場合はnullが返されます。
> const re = /#/y; re.lastIndex = 1;
> [re.exec('##-#'), re.lastIndex]
[{ 0: '#', index: 1, input: '##-#' }, 2]
> [re.exec('##-#'), re.lastIndex]
[null, 0]/ygがある場合、.exec()は/yの場合と同じ動作をします。
regExp.test(str) [ES3]このメソッドは.exec()と同じ動作をしますが、マッチオブジェクトを返す代わりにtrueを返し、nullを返す代わりにfalseを返します。
例えば、/gも/yもない場合、結果は常にtrueになります。
> const re = /#/; re.lastIndex = 1;
> [re.test('##-#'), re.lastIndex]
[true, 1]
> [re.test('##-#'), re.lastIndex]
[true, 1]/gがある場合、2つのマッチがあります。
> const re = /#/g; re.lastIndex = 1;
> [re.test('##-#'), re.lastIndex]
[true, 2]
> [re.test('##-#'), re.lastIndex]
[true, 4]
> [re.test('##-#'), re.lastIndex]
[false, 0]/yがある場合、1つのマッチしかありません。
> const re = /#/y; re.lastIndex = 1;
> [re.test('##-#'), re.lastIndex]
[true, 2]
> [re.test('##-#'), re.lastIndex]
[false, 0]/ygがある場合、.test()は/yの場合と同じ動作をします。
str.match(regExp) [ES3]/gがない場合、.match()は.exec()のように動作します。/yがない場合、または
> const re = /#/; re.lastIndex = 1;
> ['##-#'.match(re), re.lastIndex]
[{ 0: '#', index: 0, input: '##-#' }, 1]
> ['##-#'.match(re), re.lastIndex]
[{ 0: '#', index: 0, input: '##-#' }, 1]/yがある場合
> const re = /#/y; re.lastIndex = 1;
> ['##-#'.match(re), re.lastIndex]
[{ 0: '#', index: 1, input: '##-#' }, 2]
> ['##-#'.match(re), re.lastIndex]
[null, 0]/gがある場合、すべてのマッチ(グループ0)が配列で返されます。.lastIndexは無視され、0にリセットされます。
> const re = /#/g; re.lastIndex = 1;
> '##-#'.match(re)
['#', '#', '#']
> re.lastIndex
0/ygは/gのように動作しますが、マッチ間のギャップはありません。
> const re = /#/yg; re.lastIndex = 1;
> '##-#'.match(re)
['#', '#']
> re.lastIndex
0str.matchAll(regExp) [ES2020]/gが設定されていない場合、.matchAll()は例外をスローします。
> const re = /#/y; re.lastIndex = 1;
> '##-#'.matchAll(re)
TypeError: String.prototype.matchAll called with
a non-global RegExp argument/gが設定されている場合、マッチングは.lastIndexから始まり、そのプロパティは変更されません。
> const re = /#/g; re.lastIndex = 1;
> Array.from('##-#'.matchAll(re))
[
{ 0: '#', index: 1, input: '##-#' },
{ 0: '#', index: 3, input: '##-#' },
]
> re.lastIndex
1/ygが設定されている場合、動作は/gの場合と同じですが、マッチ間のギャップはありません。
> const re = /#/yg; re.lastIndex = 1;
> Array.from('##-#'.matchAll(re))
[
{ 0: '#', index: 1, input: '##-#' },
]
> re.lastIndex
1str.replace(regExp, str) [ES3]/gと/yがない場合、最初の出現箇所だけが置換されます。
> const re = /#/; re.lastIndex = 1;
> '##-#'.replace(re, 'x')
'x#-#'
> re.lastIndex
1/gがある場合、すべての出現箇所が置換されます。.lastIndexは無視されますが、0にリセットされます。
> const re = /#/g; re.lastIndex = 1;
> '##-#'.replace(re, 'x')
'xx-x'
> re.lastIndex
0/yがある場合、.lastIndexにある(最初の)出現箇所だけが置換されます。.lastIndexは更新されます。
> const re = /#/y; re.lastIndex = 1;
> '##-#'.replace(re, 'x')
'#x-#'
> re.lastIndex
2/ygは/gのように動作しますが、マッチ間のギャップは許されません。
> const re = /#/yg; re.lastIndex = 1;
> '##-#'.replace(re, 'x')
'xx-#'
> re.lastIndex
0str.replaceAll(regExp, str) [ES2021].replaceAll()は.replace()のように動作しますが、/gが設定されていない場合は例外をスローします。
> const re = /#/y; re.lastIndex = 1;
> '##-#'.replaceAll(re, 'x')
TypeError: String.prototype.replaceAll called
with a non-global RegExp argument/gと/yの4つの落とし穴とその対処法まず、/gと/yの4つの落とし穴、そしてそれらの落とし穴に対処する方法を見ていきます。
/gまたは/y付きの正規表現をインライン化できません/g付きの正規表現はインライン化できません。例えば、以下のwhileループでは、条件がチェックされるたびに正規表現が新しく作成されます。そのため、その.lastIndexは常に0になり、ループは終了しません。
let matchObj;
// Infinite loop
while (matchObj = /a+/g.exec('bbbaabaaa')) {
console.log(matchObj[0]);
}/yの場合も、問題は同じです。
/gまたは/yを削除するとコードが壊れる可能性がありますコードが/g付きの正規表現を期待しており、.exec()または.test()の結果をループしている場合、/gのない正規表現は無限ループを引き起こす可能性があります。
function collectMatches(regExp, str) {
const matches = [];
let matchObj;
// Infinite loop
while (matchObj = regExp.exec(str)) {
matches.push(matchObj[0]);
}
return matches;
}
collectMatches(/a+/, 'bbbaabaaa'); // Missing: flag /g無限ループになるのはなぜでしょうか?それは、.exec()が常に最初の結果であるマッチオブジェクトを返し、nullを返さないためです。
/yの場合も、問題は同じです。
/gまたは/yを追加するとコードが壊れる可能性があります.test()にはもう1つの注意点があります。それは.lastIndexの影響を受けるということです。したがって、正規表現が文字列に一致するかどうかを正確に1回だけチェックしたい場合、正規表現には/gを含めるべきではありません。そうでなければ、.test()を呼び出すたびに一般的に異なる結果が得られます。
> const regExp = /^X/g;
> [regExp.test('Xa'), regExp.lastIndex]
[ true, 1 ]
> [regExp.test('Xa'), regExp.lastIndex]
[ false, 0 ]
> [regExp.test('Xa'), regExp.lastIndex]
[ true, 1 ]最初の呼び出しはマッチを行い.lastIndexを更新します。2回目の呼び出しはマッチを見つけず、.lastIndexを0にリセットします。
.test()専用に正規表現を作成する場合、おそらく/gは追加しません。しかし、置換とテストの両方で同じ正規表現を使用する場合、/gに遭遇する可能性は高まります。
繰り返しますが、この問題は/yにも存在します。
> const regExp = /^X/y;
> regExp.test('Xa')
true
> regExp.test('Xa')
false
> regExp.test('Xa')
true.lastIndexが0でない場合、コードは予期しない結果を生成する可能性があります.lastIndexの影響を受けるすべての正規表現操作を考えると、多くのアルゴリズムにおいて、.lastIndexが最初に0であることを注意深く確認する必要があります。そうでなければ、予期しない結果になる可能性があります。
function countMatches(regExp, str) {
let count = 0;
while (regExp.test(str)) {
count++;
}
return count;
}
const myRegExp = /a/g;
myRegExp.lastIndex = 4;
assert.equal(
countMatches(myRegExp, 'babaa'), 1); // should be 3通常、新しく作成された正規表現の.lastIndexは0であり、例のように明示的に変更することはありません。しかし、正規表現を複数回使用すると、.lastIndexが0でないままになる可能性があります。
/gと/yの落とし穴を回避する方法/gと.lastIndexの対処方法の例として、前の例のcountMatches()を再訪します。間違った正規表現がコードを壊すのを防ぐにはどうすればよいでしょうか?3つのアプローチを見てみましょう。
まず、/gが設定されていないか、.lastIndexが0でない場合に例外をスローできます。
function countMatches(regExp, str) {
if (!regExp.global) {
throw new Error('Flag /g of regExp must be set');
}
if (regExp.lastIndex !== 0) {
throw new Error('regExp.lastIndex must be zero');
}
let count = 0;
while (regExp.test(str)) {
count++;
}
return count;
}次に、パラメータをクローンできます。これには、regExpが変更されないという追加の利点があります。
function countMatches(regExp, str) {
const cloneFlags = regExp.flags + (regExp.global ? '' : 'g');
const clone = new RegExp(regExp, cloneFlags);
let count = 0;
while (clone.test(str)) {
count++;
}
return count;
}.lastIndexまたはフラグの影響を受けない操作を使用するいくつかの正規表現操作は、.lastIndexまたはフラグの影響を受けません。例えば、.match()は/gが存在する場合、.lastIndexを無視します。
function countMatches(regExp, str) {
if (!regExp.global) {
throw new Error('Flag /g of regExp must be set');
}
return (str.match(regExp) ?? []).length;
}
const myRegExp = /a/g;
myRegExp.lastIndex = 4;
assert.equal(countMatches(myRegExp, 'babaa'), 3); // OK!ここでは、.lastIndexをチェックまたは修正していなくても、countMatches()は動作します。
.lastIndexのユースケース:特定のインデックスからマッチングを開始する状態を保存する以外にも、.lastIndexは特定のインデックスからマッチングを開始するためにも使用できます。このセクションでは、その方法について説明します。
.test()は/yと.lastIndexの影響を受けるため、特定のindexで正規表現regExpが文字列strと一致するかどうかをチェックするために使用できます。
function matchesStringAt(regExp, str, index) {
if (!regExp.sticky) {
throw new Error('Flag /y of regExp must be set');
}
regExp.lastIndex = index;
return regExp.test(str);
}
assert.equal(
matchesStringAt(/x+/y, 'aaxxx', 0), false);
assert.equal(
matchesStringAt(/x+/y, 'aaxxx', 2), true);regExpは/yのために.lastIndexに固定されています。
入力文字列の先頭にregExpを固定するアサーション^を使用してはならないことに注意してください。
.search()を使用すると、正規表現が一致する場所を見つけることができます。
> '#--#'.search(/#/)
0しかし、.search()がマッチの検索を開始する場所を変更することはできません。回避策として、検索には.exec()を使用できます。
function searchAt(regExp, str, index) {
if (!regExp.global && !regExp.sticky) {
throw new Error('Either flag /g or flag /y of regExp must be set');
}
regExp.lastIndex = index;
const match = regExp.exec(str);
if (match) {
return match.index;
} else {
return -1;
}
}
assert.equal(
searchAt(/#/g, '#--#', 0), 0);
assert.equal(
searchAt(/#/g, '#--#', 1), 3);/gなしで/yを使用して使用する場合、.replace()は1つの置換を行います(.lastIndexにマッチがある場合)。
function replaceOnceAt(str, regExp, replacement, index) {
if (!(regExp.sticky && !regExp.global)) {
throw new Error('Flag /y must be set, flag /g must not be set');
}
regExp.lastIndex = index;
return str.replace(regExp, replacement);
}
assert.equal(
replaceOnceAt('aa aaaa a', /a+/y, 'X', 0), 'X aaaa a');
assert.equal(
replaceOnceAt('aa aaaa a', /a+/y, 'X', 3), 'aa X a');
assert.equal(
replaceOnceAt('aa aaaa a', /a+/y, 'X', 8), 'aa aaaa X');.lastIndexの欠点正規表現のプロパティ.lastIndexには、2つの重要な欠点があります。
.lastIndexのサポートは、正規表現操作間で一貫性がありません。良い面としては、.lastIndexは追加の便利な機能も提供します。マッチングを開始する場所を指定できます(一部の操作の場合)。
.global(/g)と.sticky(/y)次の2つのメソッドは、/gと/yの両方から完全に影響を受けません。
String.prototype.search()String.prototype.split()この表は、残りの正規表現関連メソッドがこれら2つのフラグによってどのように影響を受けるかを示しています。
/ |
/g |
/y |
/yg |
|
|---|---|---|---|---|
r.exec(s) |
{i:0} |
{i:1} |
{i:1} |
{i:1} |
.lI変更なし |
.lI更新 |
.lI更新 |
.lI更新 |
|
r.test(s) |
true |
true |
true |
true |
.lI変更なし |
.lI更新 |
.lI更新 |
.lI更新 |
|
s.match(r) |
{i:0} |
["#","#","#"] |
{i:1} |
["#","#"] |
.lI変更なし |
.lIリセット |
.lI更新 |
.lIリセット |
|
s.matchAll(r) |
TypeError |
[{i:1}, {i:3}] |
TypeError |
[{i:1}] |
.lI変更なし |
.lI変更なし |
|||
s.replace(r, 'x') |
"x#-#" |
"xx-x" |
"#x-#" |
"xx-#" |
.lI変更なし |
.lIリセット |
.lI更新 |
.lIリセット |
|
s.replaceAll(r, 'x') |
TypeError |
"xx-x" |
TypeError |
"xx-#" |
.lIリセット |
.lIリセット |
変数
const r = /#/; r.lastIndex = 1;
const s = '##-#';略語
{i:2}:プロパティ.indexの値が2であるマッチオブジェクト.lI更新:.lastIndexが更新されます.lIリセット:.lastIndexが0にリセットされます.lI変更なし:.lastIndexは変更されません 前の表を生成したNode.jsスクリプト
前の表は、Node.jsスクリプトによって生成されました。
次の関数は、任意のテキストをエスケープします。これにより、正規表現内に配置した場合、テキストがそのまま一致するようになります。
function escapeForRegExp(str) {
return str.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); // (A)
}
assert.equal(escapeForRegExp('[yes?]'), String.raw`\[yes\?\]`);
assert.equal(escapeForRegExp('_g_'), String.raw`_g_`);A行では、すべての構文文字をエスケープします。正規表現フラグ/uは多くのエスケープ(例:\a \: \-)を禁止するため、選択的に行う必要があります。
escapeForRegExp()には2つのユースケースがあります。
new RegExp()を使用して作成する正規表現にプレーンテキストを挿入したい場合。.replace()を使用してプレーンテキスト文字列のすべての出現箇所を置換したい場合(.replaceAll()は使用できません)。.replace()では、プレーンテキストを一度だけ置換できます。escapeForRegExp()を使用すると、この制限を回避できます。
const plainText = ':-)';
const regExp = new RegExp(escapeForRegExp(plainText), 'ug');
assert.equal(
':-) :-) :-)'.replace(regExp, '🙂'), '🙂 🙂 🙂');場合によっては、すべてまたは何もマッチしない正規表現が必要になる場合があります(デフォルト値など)。
すべてにマッチ:/(?:)/
空のグループ()はすべてにマッチします。不要な作業を避けるために、非キャプチャ型(?:を使用)にします。
> /(?:)/.test('')
true
> /(?:)/.test('abc')
true何もマッチしない:/.^/
^は文字列の先頭でのみマッチします。ドットはマッチングを最初の文字以降に移動し、^はもはやマッチしなくなります。
> /.^/.test('')
false
> /.^/.test('abc')
false