この記事では、正規表現を扱う様々なメソッドを詳細に解説します。
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など)を使用します。