この記事では、正規表現を扱う様々なメソッドを詳細に解説します。
str.match(regexp)
str.match(regexp)
メソッドは、文字列str
内でregexp
に一致するものを検索します。
このメソッドには3つのモードがあります。
-
regexp
にフラグg
がない場合、最初のマッチを、キャプチャグループとプロパティindex
(マッチの位置)、input
(入力文字列、str
と同じ)を持つ配列として返します。let str = "I love JavaScript"; let result = str.match(/Java(Script)/); alert( result[0] ); // JavaScript (full match) alert( result[1] ); // Script (first capturing group) alert( result.length ); // 2 // Additional information: alert( result.index ); // 7 (match position) alert( result.input ); // I love JavaScript (source string)
-
regexp
にフラグg
がある場合、すべてのマッチを文字列の配列として返し、キャプチャグループやその他の詳細は含まれません。let str = "I love JavaScript"; let result = str.match(/Java(Script)/g); alert( result[0] ); // JavaScript alert( result.length ); // 1
-
マッチがない場合、フラグ
g
の有無にかかわらず、null
が返されます。これは重要なニュアンスです。マッチがない場合、空の配列ではなく、
null
が返されます。これを忘れてしまうと、例えば以下のようなミスを犯しやすいです。let str = "I love JavaScript"; let result = str.match(/HTML/); alert(result); // null alert(result.length); // Error: Cannot read property 'length' of null
結果を配列にしたい場合は、次のように書くことができます。
let result = str.match(regexp) || [];
str.matchAll(regexp)
str.matchAll(regexp)
メソッドは、str.match
の「新しい、改良された」バージョンです。
これは主に、すべてのグループを含むすべてのマッチを検索するために使用されます。
match
との違いは3つあります。
- 配列ではなく、マッチを含む反復可能オブジェクトを返します。
Array.from
を使用して、通常の配列を作成できます。 - すべてのマッチは、キャプチャグループを持つ配列として返されます(フラグ
g
のないstr.match
と同じ形式)。 - 結果がない場合は、
null
ではなく、空の反復可能オブジェクトを返します。
使用例
let str = '<h1>Hello, world!</h1>';
let regexp = /<(.*?)>/g;
let matchAll = str.matchAll(regexp);
alert(matchAll); // [object RegExp String Iterator], not array, but an iterable
matchAll = Array.from(matchAll); // array now
let firstMatch = matchAll[0];
alert( firstMatch[0] ); // <h1>
alert( firstMatch[1] ); // h1
alert( firstMatch.index ); // 0
alert( firstMatch.input ); // <h1>Hello, world!</h1>
for..of
を使用してmatchAll
のマッチをループする場合、Array.from
は不要です。
str.split(regexp|substr, limit)
正規表現(または部分文字列)を区切り文字として使用して、文字列を分割します。
split
は、次のように文字列で使用できます。
alert('12-34-56'.split('-')) // array of ['12', '34', '56']
しかし、同じように正規表現で分割することもできます。
alert('12, 34, 56'.split(/,\s*/)) // array of ['12', '34', '56']
str.search(regexp)
str.search(regexp)
メソッドは、最初のマッチの位置を返し、見つからない場合は-1
を返します。
let str = "A drop of ink may make a million think";
alert( str.search( /ink/i ) ); // 10 (first match position)
重要な制限事項:search
は最初のマッチのみを見つけます。
それ以降のマッチの位置が必要な場合は、str.matchAll(regexp)
ですべてのマッチを見つけるなど、他の方法を使用する必要があります。
str.replace(str|regexp, str|func)
これは、検索と置換のための汎用的なメソッドであり、最も便利なメソッドの1つです。検索と置換のための万能ナイフです。
正規表現を使用せずに、部分文字列を検索して置換するために使用できます。
// replace a dash by a colon
alert('12-34-56'.replace("-", ":")) // 12:34-56
ただし、落とし穴があります。
replace
の最初の引数が文字列の場合、最初のマッチのみが置換されます。
上記の例では、最初の"-"
のみが":"
に置き換えられています。
すべてのハイフンを見つけるには、文字列"-"
ではなく、必須のg
フラグが付いた正規表現/-/g
を使用する必要があります。
// replace all dashes by a colon
alert( '12-34-56'.replace( /-/g, ":" ) ) // 12:34:56
2番目の引数は置換文字列です。 इसमें 特殊文字を使用できます。
記号 | 置換文字列での動作 |
---|---|
$& |
マッチ全体を挿入します |
$` |
マッチの前の文字列の一部を挿入します |
$' |
マッチの後の文字列の一部を挿入します |
$n |
n が1桁または2桁の数字の場合、n番目のキャプチャグループの内容を挿入します。詳細はキャプチャグループを参照してください。 |
$<name> |
指定されたname を持つ括弧の内容を挿入します。詳細はキャプチャグループを参照してください。 |
$$ |
文字$ を挿入します |
例えば
let str = "John Smith";
// swap first and last name
alert(str.replace(/(john) (smith)/i, '$2, $1')) // Smith, John
「スマートな」置換が必要な場合は、2番目の引数を関数にすることができます。
これは各マッチに対して呼び出され、返された値が置換として挿入されます。
関数は、引数func(match, p1, p2, ..., pn, offset, input, groups)
で呼び出されます。
match
- マッチp1, p2, ..., pn
- キャプチャグループの内容(存在する場合)offset
- マッチの位置input
- ソース文字列groups
- 名前付きグループを持つオブジェクト
正規表現に括弧がない場合、引数は3つだけです:func(str, offset, input)
。
例えば、すべてのマッチを大文字にします。
let str = "html and css";
let result = str.replace(/html|css/gi, str => str.toUpperCase());
alert(result); // HTML and CSS
各マッチを文字列内の位置に置き換えます。
alert("Ho-Ho-ho".replace(/ho/gi, (match, offset) => offset)); // 0-3-6
以下の例では括弧が2つあるため、置換関数は5つの引数で呼び出されます。最初は完全なマッチ、次に2つの括弧、そしてその後に(例では使用されていません)マッチの位置とソース文字列が続きます。
let str = "John Smith";
let result = str.replace(/(\w+) (\w+)/, (match, name, surname) => `${surname}, ${name}`);
alert(result); // Smith, John
グループが多い場合は、レストパラメータを使用してアクセスすると便利です。
let str = "John Smith";
let result = str.replace(/(\w+) (\w+)/, (...match) => `${match[2]}, ${match[1]}`);
alert(result); // Smith, John
または、名前付きグループを使用している場合、それらを持つgroups
オブジェクトは常に最後にあるため、次のように取得できます。
let str = "John Smith";
let result = str.replace(/(?<name>\w+) (?<surname>\w+)/, (...match) => {
let groups = match.pop();
return `${groups.surname}, ${groups.name}`;
});
alert(result); // Smith, John
関数を使用すると、マッチに関するすべての情報が取得され、外部変数にアクセスでき、すべてを実行できるため、究極の置換能力が得られます。
str.replaceAll(str|regexp, str|func)
このメソッドは基本的にstr.replace
と同じですが、2つの大きな違いがあります。
- 最初の引数が文字列の場合、文字列の*すべて*の出現箇所を置換しますが、
replace
は*最初*の出現箇所のみを置換します。 - 最初の引数が
g
フラグのない正規表現の場合、エラーが発生します。g
フラグを付けると、replace
と同じように動作します。
replaceAll
の主なユースケースは、文字列のすべての出現箇所を置換することです。
このように
// replace all dashes by a colon
alert('12-34-56'.replaceAll("-", ":")) // 12:34:56
regexp.exec(str)
regexp.exec(str)
メソッドは、文字列str
内のregexp
のマッチを返します。前のメソッドとは異なり、文字列ではなく正規表現に対して呼び出されます。
正規表現にフラグg
があるかどうかによって、動作が異なります。
g
がない場合、regexp.exec(str)
はstr.match(regexp)
とまったく同じように最初のマッチを返します。この動作は新しいものを何ももたらしません。
しかし、フラグg
がある場合、
regexp.exec(str)
の呼び出しは、最初のマッチを返し、その直後の位置をプロパティregexp.lastIndex
に保存します。- 次の呼び出しは、位置
regexp.lastIndex
から検索を開始し、次のマッチを返し、その後の位置をregexp.lastIndex
に保存します。 - ...など。
- マッチがない場合、
regexp.exec
はnull
を返し、regexp.lastIndex
を0
にリセットします。
そのため、繰り返しの呼び出しは、プロパティregexp.lastIndex
を使用して現在の検索位置を追跡することで、すべてのマッチを次々に返します。
以前、str.matchAll
メソッドがJavaScriptに追加される前は、ループ内でregexp.exec
の呼び出しを使用して、グループを含むすべてのマッチを取得していました。
let str = 'More about JavaScript at https://javascriptinfo.dokyumento.jp';
let regexp = /javascript/ig;
let result;
while (result = regexp.exec(str)) {
alert( `Found ${result[0]} at position ${result.index}` );
// Found JavaScript at position 11, then
// Found javascript at position 33
}
これは現在でも機能しますが、新しいブラウザでは通常、str.matchAll
の方が便利です。
lastIndex
を手動で設定することで、指定された位置から検索するためにregexp.exec
を使用できます。
例えば
let str = 'Hello, world!';
let regexp = /\w+/g; // without flag "g", lastIndex property is ignored
regexp.lastIndex = 5; // search from 5th position (from the comma)
alert( regexp.exec(str) ); // world
正規表現にフラグy
がある場合、検索は正確に位置regexp.lastIndex
で実行され、それ以上は実行されません。
上記の例でフラグg
をy
に置き換えてみましょう。位置5
に単語がないため、マッチはありません。
let str = 'Hello, world!';
let regexp = /\w+/y;
regexp.lastIndex = 5; // search exactly at position 5
alert( regexp.exec(str) ); // null
これは、文字列から正規表現によって何かを正確な位置で「読み取る」必要がある場合に便利です。
regexp.test(str)
regexp.test(str)
メソッドは、マッチを検索し、それが存在するかどうかをtrue/false
で返します。
例えば
let str = "I love JavaScript";
// these two tests do the same
alert( /love/i.test(str) ); // true
alert( str.search(/love/i) != -1 ); // true
否定的な回答の例
let str = "Bla-bla-bla";
alert( /love/i.test(str) ); // false
alert( str.search(/love/i) != -1 ); // false
正規表現にフラグg
がある場合、regexp.test
はregexp.exec
と同様に、regexp.lastIndex
プロパティから検索し、このプロパティを更新します。
そのため、指定された位置から検索するために使用できます。
let regexp = /love/gi;
let str = "I love JavaScript";
// start the search from position 10:
regexp.lastIndex = 10;
alert( regexp.test(str) ); // false (no match)
同じグローバル正規表現を異なる入力に適用すると、regexp.test
の呼び出しによってregexp.lastIndex
プロパティが進むため、別の文字列の検索がゼロ以外の位置から開始される可能性があり、間違った結果につながる可能性があります。
例えば、ここでは同じテキストに対してregexp.test
を2回呼び出していますが、2回目は失敗します。
let regexp = /javascript/g; // (regexp just created: regexp.lastIndex=0)
alert( regexp.test("javascript") ); // true (regexp.lastIndex=10 now)
alert( regexp.test("javascript") ); // false
これはまさに、2回目のテストでregexp.lastIndex
がゼロ以外であるためです。
これを回避するには、各検索の前にregexp.lastIndex = 0
を設定します。または、正規表現のメソッドを呼び出す代わりに、文字列メソッドstr.match/search/...
を使用します。これらのメソッドはlastIndex
を使用しません。
コメント
<code>
タグを使用します。数行の場合は、<pre>
タグで囲みます。10行を超える場合は、サンドボックス(plnkr、jsbin、codepenなど)を使用します。