【Javascript基礎】正規表現（RegExp）オブジェクトにしっかり向き合ってみた。

Javascriptを学習中、今までなんとなくで利用していた正規表現をしっかり学習する機会があったので、復習も兼ねて投稿します。

# ◆正規表現とは
<font color="blue">曖昧な文字列パターンを検索する仕組みのこと。</font>

**例）**
以下の文章から、郵便番号だけを取り出したいとする。

**現在の郵便番号は`111-1111`、以前は`999-9999`。**

この時、郵便番号を正規表現で表すと、

**<font color="blue">[0-9]{3}-[0-9]{4}</font>**

となる。
またこのように正規表現で表された文字列パターンを`正規表現パターン`という。

# ◆主な正規表現パターン一覧

**【 基本 】**
| パターン | マッチする文字列 |
|:-|:-|
|ABC   |ABC という文字列   |
|[ABC]   |A,B,C のいずれか1文字   |
|[^ABC]   |A,B,C 以外のいずれか1文字   |
|[A-Z]   |A~Z の間の1文字   |
|A\|B\|C   |A,B,C のいずれか（AB, AC, BCでも可）   |

**【 量指定 】**
|パターン  |マッチする文字列  |
|:-|:-|
|X*   |0文字以上のX。（AB* ⇨ A, AB, ABBなど）   |
|X?   |0,または1文字のX。（AB? ⇨ A, AB ※ABBにはマッチしない）   |
|X+   |1文字以上のX。（AB+ → AB, ABBなど ※A,Bにはマッチしない）   |
|X{n} |Xとn回一致。（[0-9]{3}　は3桁の数字）   |
|X{n,}|Xとn回以上一致。（[0-9]{3,}　は3桁以上の数字）   |
|X{m,n}|Xとm~n回一致。（[0-9]{3,5}　は3~5桁の数字）   |

**【 位置指定 】**
|パターン  |マッチする文字列  |
|:-|:-|
|^   |文字列の先頭の1文字と一致(mフラグ適用時に行頭と一致)   |
|$   |文字列の末尾の1文字に一致(mフラグ適用時に行末と一致)   |


**【 文字セット 】**
|パターン  |マッチする文字列  |
|:-|:-|
|.  |改行以外の任意の1文字。（sフラグを指定する事で改行を含む任意の1文字）  |
|\w|大文字/小文字の英字、数字、アンダースコアに一致。（[A-Za-z0-9_]と同じ）|
|\W|文字以外に一致。（[^\w]と同じ）|
|\d|数字1文字に一致。（[0-9]と同じ）|
|\D|数字以外の1文字に一致。（[^0-9]と同じ）|
|\n|改行1文字|
|\r|復帰コード1文字（行末から行頭に戻す復帰コード。キャリッジリターン）|
|\t|タブ文字の1文字に一致。|
|\s|空白文字の1文字に一致。|
|\S|空白文字以外の1文字に一致。|

**【 オプション 】**
|オプション  |概要  |
|:-|:-|
|g|文字全体に対してマッチするか（無指定の場合、一度マッチした地点で処理が終了）|
|i|大文字/小文字を区別するか|
|m|複数行に対応するか（改行コードを行頭/行末と認識）|
|u|Unicode対応|

# ◆正規表現オブジェクトの生成方法
正規表現オブジェクトを生成する方法は主に２種類。

```javascript
// ①正規表現オブジェクト（RegExpオブジェクト）のコンストラクターを経由する。
let reg = new RegExp('正規表現', 'オプション');


// ②正規表現リテラルを利用する。
 let reg = /正規表現/オプション;
```
## ・ 正規表現オブジェクトのコンストラクターで生成する場合
**【 ポイント 】**

**① 第一引数に`正規表現`、第二引数に`オプション`。**

→ コンストラクター構文では、正規表現を<font color="red">文字列</font>として指定するので、シングルクォーテーション（もしくはダブルクォーテーション）で囲むこと。

**② 正規表現部分の中に`\（バックスラッシュ）`が含まれている場合、<font color="red">さらに`\（バックスラッシュ）`を追加してエスケープ処理する必要がある。</font>**

→ コンストラクター構文では、正規表現は全て文字列として区別される。\（バックスラッシュ）自体は予約文字なので、バックスラッシュも含めた正規表現を利用するには、`追加で\（バックスラッシュ）`をつけてエスケープ処理する。

**例）**
`文字以外の一致（\W）を表現したい場合`
```javascript
const reg = new RegExp('\\W');
```

## ・ 正規表現リテラルでの生成の場合
**【 ポイント 】**

**① 正規表現の部分は`/（スラッシュ）`で囲む必要がある。**

→ 最初のスラッシュと最後のスラッシュで囲まれた中身を、正規表現として理解される。

**② 正規表現部分の中に`/（スラッシュ）`が含まれている場合、<font color="red">`\（バックスラッシュ）`でエスケープ処理する必要がある。</font>**

→ /（スラッシュ）は、正規表現の始まりと終わりを意味するので、/（スラッシュ）を正規表現として利用したいのであれば、\（バックスラッシュ）でエスケープ処理が必須。

**例）**
`http://という文字列を正規表現で表したい場合`
```javascript
const reg = /http:\/\//;

const str = "~";

str.match(reg);

// return　はマッチした文字列を配列として返す。
```

# ◆正規表現の検索
javascriptの正規表現では、`match`メソッドを利用するのが主流。

**例）** 
**URL文字列を取得してみる**
```javascript
let regExp = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?/gi;

let url = "URLは、http://www.wings.msn.to/";

url += "と、HTTP://www.web-deli.com/";

console.log(url);
// URLは、http://www.wings.msn.to/と、HTTP://www.web-deli.com/

const result = url.match(regExp);

for(let i=0; i < result.length; i++) {
 console.log(result[i]);
}
// http://www.wings.msn.to/
// HTTP://www.web-deli.com/
```
**・ 正規表現パターンの内訳**
| 正規表現 | マッチする文字列 |
|:-:|:-:|
| http(s)?:\/\/  | 「http://」もしくは「https://」  |
| ([\w-]+\.)  | 「www.」、「wings.」、「msn.」、「web-deli.」 |
| +[\w-]  | 「to」、「com」  |
| (\/[\w-.\/?%&=]*)?  |/   |

オプションは「◆主な正規表現パターン一覧」に記載。


# ◆正規表現のマッチ判定
正規表現で、マッチする文字列があるかどうか（true,false）を判定したい場合は、`test`メソッドを使用する。
```javascript
const reg = /http:\/\//;

const str = "http://";

const str2 = "~";

reg.test(str);
// true

reg.test(str2);

// false
// matchメソッドと違い、正規表現パターンに対してメソッドを使用している。
```

# ◆正規表現が最初にマッチした場所を知りたい
正規表現パターンがある文字列に対してマッチした際、一番最初にどこの文字でマッチしたのかを知りたい場合は、`search`メソッドを使用する。
```javascript
const regExp = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?/gi;

const url = "URLは、http://www.wings.msn.to/";

console.log(url.search(regExp));
// 5
// 一番最初にマッチした文字のインデックス番号を返す。（最初にマッチしたhはインデックス番号で５番目）
```

# ◆正規表現で文字列を置き換える
`String.replace`メソッドを利用することで、正規表現でマッチした文字列を置換することができる。
```javascript
str.replace(pattern, rep);
/**
 * str    　→ 置き換えたい対象の文字列
 * 
 * pattern　→ 正規表現
 * 
 * rep    　→ 置き換え後の文字列
 */
```

**例）**
**文字列に含まれるURL文字列をアンカータグに置き換える**
```javascript
const regExp = /(http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?)/gi;

let url = "URLは、http://www.wings.msn.to/";

document.write(url.replace(regExp, '<a href="$1">$1</a>'));
```
**結果**
![スクリーンショット 2022-10-10 13.31.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754263/e97cbeda-64ee-62d8-ce02-43c80e99c06d.png)

## $1について
$1は、特殊変数。この変数は、**<font color="red">サブマッチ文字列</font>** を保存するための変数。

**<font color="red">サブマッチ文字列</font>** とは、
**正規表現パターンの中で、丸括弧（）で示された部分のこと。**

つまり、`(s)`、`([\w-]+\.)`、`(\/[\w-.\/?%&=]*)`は全てサブマッチ文字列に該当する。
今回の場合はURL文字列全体をサブマッチ文字列として判定し、リンクをそのまま$1に入れたいので、**正規表現パターン全体を丸括弧で囲っている。**
```javascript
/ ( http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)? ) /
```
ちなみに今回の場合、特殊変数には以下の情報が入っている。
| 変数 | 格納されている値 |
|:-|:-|
| $1  | http:\/\/www.wings.msn.to/   |
| $2  |undefined　（sはリンク文字列にないから）   |
| $3  |msn.（複数マッチした場合は、最後のものが格納される）   |
| $4  | /  |

# ◆正規表現で文字列を分割
正規表現で文字列を分割するには、`String.split`メソッドを使用する。
```javascript
str.split(regExp);

/**
 * str → 対象の文字列
 * 
 * regExp　→ 区切り文字（正規表現）
 *
 * return 分割した結果を配列で返す
 */
```
**例）**
```javascript
const regExp = /[\/\.\-]/gi;

console.log("2022/01/01".split(regExp));  // ["2022", "01", "01"]

console.log("2022.01.01".split(regExp));  // ["2022", "01", "01"]

console.log("2022-01-01".split(regExp));  // ["2022", "01", "01"]
```


# ◆参考文献

https://gihyo.jp/book/2016/978-4-7741-8411-1

https://qiita.com/iLLviA/items/b6bf680cd2408edd050f