【JavaScript】文字列④ 〜正規表現オブジェクト〜

# 概要
JavaScriptを学習、理解を深めるため「[JavaScript Primer 迷わないための入門書](https://jsprimer.net/)」を読み、
理解した内容等を記載していく。

「[【JavaScript】JavaScript入門一覧](https://qiita.com/andota05/items/22fae452225b785fd405)」に他の記事をまとめています。

# この記事で理解できること
* 正規表現を用いた文字列の検索方法

# 正規表現による検索
* 文字列による検索に比べ、柔軟な検索が可能となる。
* 検索する`パターン`と、検索モードを指定する`フラグ`から構成される。
* 特殊文字`\ ^ $ . * + ? ( ) [ ] { } |`のいずれかを`パターン`に含める場合はエスケープ(`\`)が必要。
* 正規表現オブジェクトを作成するには、`正規表現リテラル`と`RegExpコンストラクタ`を使用する2つの方法がある。

```js
/* それぞれフラグは必須でないことに留意 **/
/* 正規表現リテラルで表現できる場合は、リテラルを利用した方が簡潔に記載できる **/

// 例：正規表現リテラル(「/ /」が正規表現リテラル、「\d」がパターン、「g」がフラグ)
const pattern1 = /\d/g;

// 例：RegExpコンストラクタ(「new RegExp」がRegExpコンストラクタ、(「"\\d"」がパターン、「"g"」がフラグ))
const pattern2 = new RegExp("\\d", "g");
```

## 正規表現リテラル
* `/`と`/`でパターンを囲う正規表現オブジェクトを作成できる。
* `/パターン/フラグ`のような構文で記載する。
* 正規表現リテラルは、`ソースコードをロード（パース）した段階`で評価される。

[MDN:フラグを用いた高度な検索](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_Expressions#advanced_searching_with_flags)

```js
// パターンに"ABC"をフラグ(i)に大文字小文字区別しないという条件を指定した正規表現オブジェクト
const pattern = /ABC/i;
```

## RegExpコンストラクタ
* `new`演算子を使用し、正規表現オブジェクトを作成する。
* 第一引数に`パターン`、第二引数に`フラグ`を指定する。
* `new RegExp(パターン, フラグ)`のような構文で記載する。
* RegExpコンストラクタを`呼び出したタイミング`で評価される。

```js
// パターンに"ABC"をフラグ(i)に大文字小文字区別しないという条件を指定した正規表現オブジェクト
const pattern = new RegExp("ABC", "i");
```

# 正規表現による検索
* 正規表現によるインデックスの取得(`search`メソッド)
* 正規表現によるマッチした文字列の取得(`match`、`matchAll`メソッド)

## 正規表現によるインデックスの取得(`search`メソッド)
* 正規表現のパターンにマッチした箇所の`インデックス`を返す。
* マッチする文字列がない場合は`-1`を返します。

```js
const str = "ABC12DEF";

// dは数字を示す特殊文字
const pattern = /\d/;

// 文字列の中から最初にマッチした数字のインデックスを取得
console.log(str.search(pattern)); // => 3
```

## 正規表現によるマッチした文字列の取得(`match`、`machAll`メソッド)

### `match`メソッド
* matchメソッドの返り値は、`index`プロパティと`input`プロパティをもった特殊な配列。
* `gフラグ`なしの場合は、最初にマッチしたものが見つかった時点で検索を終了する。
* `gフラグ`ありの場合は、マッチしたすべての結果が配列として返される。(`index`、`input`プロパティはなし)
* マッチした文字列がない場合は、`null`が返される。

```js
const str1 = "ABC112DEF";
const str2 = "ABCDEF";

// dは数字を示す特殊文字
const pattern1 = /\d/;
const pattern2 = /\d/g;

// 文字列の中から最初にマッチした結果を取得
console.log(str1.match(pattern1));
// => [ '1', index: 3, input: 'ABC112DEF', groups: undefined ] ※groupsは名前付きキャプチャグループ

// 文字列の中からマッチした全ての結果を取得
console.log(str1.match(pattern2));
// => [ '1', '1', '2' ]

console.log(str2.match(pattern1)); // => null
console.log(str2.match(pattern2)); // => null
```

### matchAllメソッド
* マッチした結果の各情報をIteratorオブジェクトとして取得できる。
* `gフラグ`ありのみ対応している。(`gフラグ`なしの場合、例外が発生する)
* `gフラグ`ありで検索するときは、`match`メソッドではなく、`matchAll`メソッドを使用する。
* `ES2020`から導入されている。 

```js
const str = "ABC112DEF";
const pattern = /\d/g;

// 文字列の中からマッチしたすべての結果を取得
const matchList = str.matchAll(pattern);

// Iteratorオブジェクトとして結果を取得できるのでループ処理で出力
for (match of matchList) {
  console.log(match)
}
// => [ '1', index: 3, input: 'ABC112DEF', groups: undefined ]
// => [ '1', index: 4, input: 'ABC112DEF', groups: undefined ]
// => [ '2', index: 5, input: 'ABC112DEF', groups: undefined ]
```

### マッチした文字列の一部を取得
* 正規表現には`キャプチャリング`という正規表現にマッチした`一部分を取り出す`方法がある。
* キャプチャが存在する場合、`match`メソッド、`matchAll`メソッドどちらも`配列にキャプチャが追加された`形で返される。

# 正規表現リテラルとRegExpコンストラクタのどちらを使用するのか
* 前項までに各々の評価されるタイミングが異なるように、用途に応じて使用することが望ましい。

```js
const str = "ABC112DEF";

const pattern1 = /(\d)/;
const pattern2 = /(\d)/g;

console.log(str.match(pattern1));
// => [ '1', '1', index: 3, input: 'ABC112DEF', groups: undefined ]

for (const result of str.matchAll(pattern2)) {
 console.log(result);  
}
// => [ '1', '1', index: 3, input: 'ABC112DEF', groups: undefined ]
// => [ '1', '1', index: 4, input: 'ABC112DEF', groups: undefined ]
// => [ '2', '2', index: 5, input: 'ABC112DEF', groups: undefined ]
```