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);
}.equal(/abc/i.flags, 'i');
assert.equal(copyAndAddFlags(/abc/i, 'g').flags, 'gi'); assert
正規表現の最上位レベルでは、次の構文文字は特殊です。これらは、バックスラッシュ(\
)をプレフィックスとして付けることでエスケープされます。
\ ^ $ . * + ? ( ) [ ] { } |
正規表現リテラルでは、スラッシュをエスケープする必要があります。
> /\//.test('/')true
new 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 FACE
General_Category
:文字を分類します。例:General_Category = Lowercase_Letter
General_Category = Currency_Symbol
White_Space
:スペース、タブ、改行文字などの見えないスペース文字をマークするために使用されます。例:White_Space = True
White_Space = False
Age
:文字が導入されたUnicode標準のバージョン。例:ユーロ記号€は、Unicode標準のバージョン2.1に追加されました。Age = 2.1
Block
:連続したコードポイントの範囲。ブロックは重複せず、名前は一意です。例:Block = Basic_Latin
(範囲U+0000..U+007F)🙂
: Block = Emoticons
(範囲U+1F600..U+1F64F)Script
:1つ以上の筆記体系で使用される文字の集合です。Script = Greek
Script = Cyrillic
Unicodeプロパティエスケープは次のようになります。
\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 0null
> '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
.equal(codePoint, codeUnits); // same string! assert
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).length1
.match()
と/g
は、正規表現のすべてのマッチを含む配列を返します。
/u
を使用しないと、ドット演算子はJavaScript文字にマッチします
> '\uD83D\uDE80'.match(/./g).length2
/u
を使用すると、量指定子は先行するUnicode文字全体に適用されます
> /^🙂{3}$/u.test('🙂🙂🙂')true
/u
を使用しないと、量指定子は先行するJavaScript文字のみに適用されます
> /^\uD83D\uDE80{3}$/.test('\uD83D\uDE80\uDE80\uDE80')true
注目すべき点
.lastIndex
だけが実際の実例プロパティです。他のすべてプロパティはゲッターを介して実装されます。.lastIndex
は唯一の変更可能なプロパティです。他のすべてのプロパティは読み取り専用です。変更したい場合は、正規表現をコピーする必要があります(詳細については、§43.1.2 「正規表現の複製と非破壊的な変更」を参照してください)。各正規表現フラグは、より長く、より記述的な名前を持つプロパティとして存在します
> /a/i.ignoreCasetrue
> /a/.ignoreCasefalse
これはフラグプロパティの完全なリストです
.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
を設定する必要があります。そうでない場合、例外がスローされます)。これは例です
.deepEqual(
assert/(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');
.equal(
assert1], 'aaaa'
matchObj[;
).equal(
assert2], 'bb'
matchObj[; )
正規表現フラグ/d
により、matchObj
には、入力文字列で番号付きグループがキャプチャされた場所を記録するプロパティ.indices
もあります
.deepEqual(
assert.indices[1], [0, 4]
matchObj;
).deepEqual(
assert.indices[2], [4, 6]
matchObj; )
名前付きグループのキャプチャは、このようにアクセスされます
const matchObj = /(?<as>a+)(?<bs>b+)/d.exec('aaaabb');
.equal(
assert.groups.as, 'aaaa');
matchObj.equal(
assert.groups.bs, 'bb'); matchObj
それらのインデックスはmatchObj.indices.groups
に格納されます
.deepEqual(
assert.indices.groups.as, [0, 4]);
matchObj.deepEqual(
assert.indices.groups.bs, [4, 6]); matchObj
マッチインデックスの重要なユースケースの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];
.add(start);
startIndices.add(end);
endIndices
}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;
}
.equal(
assertpointToQuotedText(
'They said “hello” and “goodbye”.'),
' [ ] [ ] '
; )
デフォルトでは、正規表現は文字列内の任意の場所にマッチします
> /a/.test('__a__')true
これは、^
などのアサーションを使用するか、/y
フラグを使用することで変更できます。
> /^a/.test('__a__')false
> /^a/.test('a__')true
regExp.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$/)4
regExp.exec(str)
:キャプチャグループ[ES3]/g
フラグがない場合、.exec()
はstr
内のregExp
の最初のマッチに対するマッチオブジェクトを返します。
.deepEqual(
assert/(a+)b/.exec('ab aab'),
{0: 'ab',
1: 'a',
index: 0,
input: 'ab aab',
groups: undefined,
}; )
前の例には、番号付きグループが1つ含まれていました。次の例は、名前付きグループを示しています。
.deepEqual(
assert/(?<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)null
null合体演算子(??
)を使用して、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倍にします。
.equal(
assert'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();
;
}).equal(result, 'FIRST=JANE, LAST=DOE'); assert
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.lastIndex0
/yg
は/g
のように動作しますが、マッチ間のギャップはありません。
> const re = /#/yg; re.lastIndex = 1;
> '##-#'.match(re)['#', '#']
> re.lastIndex0
str.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.lastIndex1
/yg
が設定されている場合、動作は/g
の場合と同じですが、マッチ間のギャップはありません。
> const re = /#/yg; re.lastIndex = 1;
> Array.from('##-#'.matchAll(re))[
{ 0: '#', index: 1, input: '##-#' },
]
> re.lastIndex1
str.replace(regExp, str)
[ES3]/g
と/y
がない場合、最初の出現箇所だけが置換されます。
> const re = /#/; re.lastIndex = 1;
> '##-#'.replace(re, 'x')'x#-#'
> re.lastIndex1
/g
がある場合、すべての出現箇所が置換されます。.lastIndex
は無視されますが、0にリセットされます。
> const re = /#/g; re.lastIndex = 1;
> '##-#'.replace(re, 'x')'xx-x'
> re.lastIndex0
/y
がある場合、.lastIndex
にある(最初の)出現箇所だけが置換されます。.lastIndex
は更新されます。
> const re = /#/y; re.lastIndex = 1;
> '##-#'.replace(re, 'x')'#x-#'
> re.lastIndex2
/yg
は/g
のように動作しますが、マッチ間のギャップは許されません。
> const re = /#/yg; re.lastIndex = 1;
> '##-#'.replace(re, 'x')'xx-#'
> re.lastIndex0
str.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)) {
.push(matchObj[0]);
matches
}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;
.lastIndex = 4;
myRegExp.equal(
assertcountMatches(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;
.lastIndex = 4;
myRegExp.equal(countMatches(myRegExp, 'babaa'), 3); // OK! assert
ここでは、.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');
}.lastIndex = index;
regExpreturn regExp.test(str);
}.equal(
assertmatchesStringAt(/x+/y, 'aaxxx', 0), false);
.equal(
assertmatchesStringAt(/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');
}.lastIndex = index;
regExpconst match = regExp.exec(str);
if (match) {
return match.index;
else {
} return -1;
}
}
.equal(
assertsearchAt(/#/g, '#--#', 0), 0);
.equal(
assertsearchAt(/#/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');
}.lastIndex = index;
regExpreturn str.replace(regExp, replacement);
}.equal(
assertreplaceOnceAt('aa aaaa a', /a+/y, 'X', 0), 'X aaaa a');
.equal(
assertreplaceOnceAt('aa aaaa a', /a+/y, 'X', 3), 'aa X a');
.equal(
assertreplaceOnceAt('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)
}.equal(escapeForRegExp('[yes?]'), String.raw`\[yes\?\]`);
assert.equal(escapeForRegExp('_g_'), String.raw`_g_`); assert
A行では、すべての構文文字をエスケープします。正規表現フラグ/u
は多くのエスケープ(例:\a \: \-
)を禁止するため、選択的に行う必要があります。
escapeForRegExp()
には2つのユースケースがあります。
new RegExp()
を使用して作成する正規表現にプレーンテキストを挿入したい場合。.replace()
を使用してプレーンテキスト文字列のすべての出現箇所を置換したい場合(.replaceAll()
は使用できません)。.replace()
では、プレーンテキストを一度だけ置換できます。escapeForRegExp()
を使用すると、この制限を回避できます。
const plainText = ':-)';
const regExp = new RegExp(escapeForRegExp(plainText), 'ug');
.equal(
assert':-) :-) :-)'.replace(regExp, '🙂'), '🙂 🙂 🙂');
場合によっては、すべてまたは何もマッチしない正規表現が必要になる場合があります(デフォルト値など)。
すべてにマッチ:/(?:)/
空のグループ()
はすべてにマッチします。不要な作業を避けるために、非キャプチャ型(?:
を使用)にします。
> /(?:)/.test('')true
> /(?:)/.test('abc')true
何もマッチしない:/.^/
^
は文字列の先頭でのみマッチします。ドットはマッチングを最初の文字以降に移動し、^
はもはやマッチしなくなります。
> /.^/.test('')false
> /.^/.test('abc')false