Regular Expression（正規表現）と フラグ

## Regular Expression（正規表現） / RegExpとは？　
RegExpは正規表現のためのJavaScriptの組み込みクラス。
**リテラル**での表記と**コンストラクタ**を利用した表記の2通りがある。
TypeScriptにはRegExp型があり、これはJavaScriptのRegExpオブジェクトを表す。

:::note 
**<font color="red"> リテラル記法 </color>**
/ で検索したい文字を囲み、最後にフラグを書く
const re = / ab+c / i ; 
:::

```typescript:例）リテラル記法
const re = /0(8|9)0-[0-9]{4}-[0-9]{4}/g;
```
-  `g（グローバルフラグ）`：文字列内のすべての一致を検索。最初の一致を見つけた後も、次の一致を検索し続ける
- `|（パイプ）`：「または」を意味し、括弧（　）でグールプ化する事で、意図しないマッチを防ぐ
- `[0-9]` ：複数の文字のいずれか一つにマッチし、ハイフン( - )を使って範囲指定を使った記述を行える
- `{ }`：数字の桁数を指定できる。{4}で4桁を表し、[0-9]{4}とする事で4桁の数字

```javascript:例）グローバルフラグ (.js)
const re = /a/g;
const str = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
```
```typescript:例）グローバルフラグ (.ts)
const re: RegExp = /a/g; // RegExp型を指定
const str: string = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
```

:::note 
**<font color="red">コンストラクタ記法 </color>**
RegExpコンストラクタに、<br>**第1引数** → 検索したい文字、**第2引数** → フラグ
- 最初の引数に文字列のパターンを渡す場合
const re = new RegExp('ab+c', 'i'); 

または、

- 最初の引数に正規表現リテラルを渡す場合
const re = new RegExp(/ab+c/, 'i'); 

:::

```javascript:例）コンストラクタ記法 (.js)
const re = new RegExp('a', 'g'); // 第1引数 → 検索したい文字、第2引数 → フラグ
const str = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
```

```typescript:例）コンストラクタ記法 (.ts)
const re: RegExp = new RegExp('a', 'g'); // RegExp型を指定
const str: string = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
```
* `matchメソッド`: 
→ gフラグがある場合： 正規表現は文字列全体に対してマッチを繰り返し検索し、すべての一致を配列として返す
→ gフラグがない場合、最初に一致した部分を含む配列を返す（マッチが1回のみで ["a"]になる）
→ 一致しない場合、null を返す
#### :warning: コンストラクタ記法ではバックスラッシュの扱いに注意する
```javascript:例）リテラル記法
const regexp_re = /0(8|9)0-\d{4}-\d{4}/g;
```
:::note warn
`\` については、コンストラクタを利用する場合はふたつ書く必要がある
:::

```javascript:例）コンストラクタ記法
const regexp_con = new RegExp("0(8|9)0-\\d{4}-\\d{4}", "g");
```
:::note warn
JavaScriptでは文字列内でバックスラッシュ `\` はエスケープ文字として使われる。
つまり、`\\` と書くことで、1つのバックスラッシュ を表現する。
:::
`\` を文字列として検索したい場合「コンストラクタでは `\\\\` と4文字書く必要がある
```javascript:例）リテラル記法
// バックスラッシュを検索したい場合
const regexp_re = /\\/;  // 1つのバックスラッシュを検索するために正規表現で
console.log(regexp_re.test("\\hoge"));  // true
```
```javascript:例）コンストラクタ記法
// バックスラッシュを検索したい場合
const regexp_con = new RegExp("\\\\"); // 文字列リテラル内で '\\' を表現
console.log(regexp_con.test("\\hoge"));  // true
```
* new RegExp("`\\\\`") では、最初の `\\` は JavaScriptの文字列リテラル内でバックスラッシュを表すために2つ必要。(文字列リテラル内でバックスラッシュをエスケープするため)
* その後、正規表現内でバックスラッシュを表すためにさらにエスケープする必要があるため、合計で4つのバックスラッシュ（`\\\\`）が必要
* `testメソッド`: 正規表現が文字列の中に1回でもマッチする場合（部分一致）にtrue を返す

## どっちを使う？
:::note
- **<font color="red"> コンストラクタ記法 </color>**
    正規表現のパターンやフラグが **動的に決まる場合**、**ユーザー入力をもとに正規表現を生成する場合**、といった動的に検索する対象を切り替えたい場合
- **<font color="red"> リテラル記法 </color>**
正規表現のパターンが事前に決まっている様な場合、特に理由がない場合
:::

## フラグ
<details><summary>g: global</summary>

```: g
検索はすべての一致を探す。ただし、gの指定がない場合は、最初の1つのみを探す。
```

```javascript: 例
const text = "123 abc 456 789";
const regex = /\d+/g;
const matches = text.match(regex);
console.log(matches);  // ["123", "456", "789"]
```
* `\d`: 半角の数字 (digit）
* `+`: 1 回以上の繰り返し
* `matchメソッド`: 正規表現に一致したすべての部分を配列で返し、なければ null を返す

---

</details>
<details><summary>i: ignoreCase</summary>

```: i
正規表現を検索する際、大文字小文字を区別ぜず検索を行う。
```

```javascript: 例
const msg = "Hello, World!";
const pattern = /WORLD/i; // i フラグは大小区別しない
const isMatch = pattern.test(msg);
console.log(isMatch); // true
```
* `testメソッド`: 正規表現が文字列の中に1回でもマッチする場合（部分一致）にtrue を返す

---

</details>
<details><summary>m: multiline</summary>

```: m
対象の文字列が複数行の場合、特定の文字列パターンの検索を行う。
行の開始 (^) や行の終了 ($) が各行の先頭や末尾にマッチするようになる。
```

```javascript:  例
const text = `Hello
World
Goodbye
World`;

const mLineStart = /^Hello/m; // ^ 行の開始　各行の先頭にマッチ
const mStart = text.match(mLineStart);
console.log(mStart); // ['Hello']

const mLineEnd = /World$/m; // $ 行の終了 各行の末尾にマッチ
const mEnd = text.match(mLineEnd);
console.log(mEnd);  // 2行目と4行目がWorldで終わっているため、["World", "World"]
```
* `matchメソッド`: 正規表現に一致したすべての部分を配列で返し、なければ null を返す

---
</details>


<details><summary>s: dotAll</summary>

```: s
正規表現のドット（.）が改行文字（\n）にもマッチするようにするフラグ。
→ 正規表現におけるドット (.) は、通常、任意の1文字を表すが、改行文字 (\n) にはマッチしないため
```

```js:改行文字 (\n)がない場合
const sentence = "father I will always be with you";
const regex = /father.*/you/;
console.log(regex.test(sentence));  // true
```
* `.`: 任意の1文字
* `*`: 直前の要素が0回以上繰り返される(直前の文字がない or 直前の文字が１個以上連続する)
* `.*`: 任意の文字が0回以上繰り返される（何でもいい文字の連続）
* `testメソッド`: 正規表現が文字列の中に1回でもマッチする場合（部分一致）にtrue を返す

```js:改行文字 (\n)がある場合
const sentence = "I am your father.\nI will always be with you."; // 改行文字を含む
const regex = /father\..*you\./;  // sフラグ未使用
console.log(regex.test(sentence));  // false
```
* `\.`: リテラルのドット（.）にマッチ

:::note warn
`.`: 正規表現におけるドット (.) は、改行文字（\n）にはマッチしない
（`.*`は改行を含まない任意の文字列にマッチするため、改行があると一致しない）
:::

```js:改行文字 (\n)がある場合、sフラグを使用
const sentence = "I am your father.\nI will always be with you."; // 改行文字を含む
const regex = /father\..*you\./s; // sフラグ使用
console.log(regex.test(sentence)); // true
```
**s フラグを使用すると、改行文字（\n）が含まれていても、正規表現のドット (.) でマッチするようになる**

---

</details>
<details><summary>u: unicode</summary>

```: u
マルチバイトの Unicode 文字を正しく扱うことができる
```
```javascript
const regex = /\u{61}/u;  // `u` フラグ付き
console.log(regex.test("a"));  // true（"a" は Unicode コードポイント 0x61 に一致する）
console.log(regex.test("b"));  // false（"b" は一致しない）

// `u`フラグなし
const regex = /\u{61}/; // "u" が 61 回連続する文字列
console.log(regex.test("u".repeat(61))); // true（"u" を 61 回繰り返した文字列は一致する）
```
* \u{xxxx} は Unicodeコードポイント を指定する方法で、{} の中にコードポイントを16進数で書く（xxxxの部分）

* `/\u{61}/u`: Unicodeコードポイント U+0061 を指し、"a" に一致

* `\u`: 単なる文字 u として扱われる（\u{61} は u{61} と同じ解釈）

* `{num}`: 直前のパターンのnum回繰り返し

```
例1) \u0041 
→ Unicodeコードポイント U+0041 を示し、「A」を表す。 
  {} を使わない形式もあり、4桁のコードポイントでよく見られる。
例2)
const regex = /^u{61}$/;  // 文字「u」を61回繰り返す文字列に一致
console.log(regex.test("u".repeat(61)));  // true
例3)
const regex = /u{61}/; // 文字「u」を61回繰り返す文字列に一致
console.log(regex.test("u".repeat(61)));  // true
```
---

</details>

<details><summary> y : sticky</summary>

```: y
RegExp オブジェクトの lastIndex プロパティが示す位置からのみマッチするかどうかを調べる。
```

```js
const adress = "東京都中央区銀座";

// yフラグを使用しない場合
const regex = /中央区/;
console.log(regex.test(adress)); // true

// yフラグを使用した場合
const regexY = /中央区/y; // 粘着検索フラグ
//console.log(regexY.lastIndex); // デフォルト 0
console.log(regexY.test(adress)); // false
//console.log(regexY.lastIndex); // 0
console.log(regexY.test(adress)); // false
```

* `testメソッド`: 正規表現が文字列の中に1回でもマッチする場合（部分一致）にtrue を返す
* `lastIndex`: 次に検索を開始すべき位置を示す

yフラグを使用すると、
* 1 回目の検索：lastIndex が示す位置から始まり、最初の検索では、lastIndex は 0 で、文字列の先頭から検索を始める。文字列 "東京都中央区銀座" の最初の部分は "東京都" で、"中央区" は最初には一致しないため、結果、falseを返す

* 2 回目の検索：前回一致しなかったため、lastIndex はリセットされ、そのまま 0 になる。そのため、検索は再び最初の位置（lastIndex = 0）から始まり、再度 "中央区" を見つけられないため、false を返す

lastIndexの位置の変更を加えてみると、以下の様に2回目の検索は trueになる。
```js
const regexY = /中央区/y; // 粘着検索フラグ
//console.log(regexY.lastIndex); // デフォルト 0
console.log(regexY.test(adress)); // false
regexY.lastIndex = 3;
console.log(regexY.test(adress)); // true
```

</details>

## 参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Regular_expressions

https://ja.javascript.info/regexp-introduction

https://www.javadrive.jp/javascript/regexp/index15.html